In this challenge, the flag is encoded by morse code and 440Hz On-Off-Keying. However, generated signals are shuffled, so we cannot decode in a normal way.

This is the distributed script:

import soundfile as sf
import numpy as np
from utils import signals_to_string, string_to_signals

signals = string_to_signals(flag)

assert(flag.startswith('TSGCTF{') and flag.endswith('}'))
assert(len(signals) == 473)

wave = np.empty(len(signals) * 2000)
for i in range(len(wave)):
if signals[i // 2000] == 0:
wave[i] = 0.0
else:
# Generate 440Hz morse code
wave[i] = np.sin(i * 440 / 44100 * (2 * np.pi))

# Fisher here :)
np.random.shuffle(wave)

sf.write('result.wav', wave, 44100, 'PCM_24')


$\sin(\lfloor \frac{440}{44100} 2\pi i \rfloor)$ produces 2205 different values, and each symbol of morse code uses 2000 of them. We cannot recover randomized signals so I totaled the number of appearance by sin value. Then I came up with that we can represent these numbers as the below simple linear equations using correlation matrix $\mathbf{A}$ and appearance count vector $\mathbf{c}$. $$\mathbf{Ax}=\mathbf{c}$$

This is my script of solving the linear system.

import numpy as np
import soundfile as sf
from utils import signals_to_string
import sys

def rev_value(v):
v = np.arcsin(v)
v /= 2 * np.pi
v *= 44100
v = int(np.round(v))
v = int(np.round(v/ 10) * 10)
return v

table = []
for i in range(5000):
k = np.sin(i * 440 / 44100 * (2 * np.pi))
table.append(rev_value(k))

dic = {}
for val in data:
val = rev_value(val)
if val not in dic:
dic[val] = 0

dic[val] += 1

indexes = set()
bucket = [ [] for i in range(473) ]
for i in range(473 * 2000):
val = np.sin(i * 440 / 44100 * (2 * np.pi))
val = rev_value(val)

if val == 0:
continue

bucket[i // 2000].append(val)

indexes = list(indexes)
rev_index = dict()
for i in range(len(indexes)):
rev_index[indexes[i]] = i

M = []
for i in range(473):
vec = [0] * len(indexes)
for j in range(len(bucket[i])):
vec[rev_index[bucket[i][j]]] = 1
M.append(vec)

vec = []
for idx in indexes:
if idx not in dic:
vec.append(0)
sys.stderr.write('aaaa' + str(idx) + '\n')
else:
vec.append(dic[idx])

A = Matrix(M)
v = vector(vec)

ans = A.transpose().solve_right(v)

print(ans)
ans = list(ans)[46:-32]
print(signals_to_string(ans))


Result:

(2, 2, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 2, 2, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0,
0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0,
1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0,
1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0,
1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1,
1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0,
1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0,
1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1,
1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1,
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
F{THE-TRUE-F1SHERM4N-U53S-M0RSE-COE


This script decodes only binary sequense in the solution because the solution includes 2 that is not used in morse code. During the contest, we guessed the flag by this fragment of the flag.

In fact, the rank of $\mathbf{A}$ is 441, and it is less than the length of the symbols. It can be recovered by using the flag format TSGCTF{, so I wrote an additional script.

from ans import ans
from utils import signals_to_string, string_to_signals

sig = string_to_signals('TSGCTF{')

ans = list(ans)

for i in range(len(sig)):
if sig[i] != ans[i]:
ans[i + 441] = ans[i] - sig[i]
ans[i] = sig[i]

print(signals_to_string(ans))


Result: TSGCTF{THE-TRUE-F1SHERM4N-U53S-M0RSE-CODE}