FCSC 2021 - Intro - File format

Table of contents :

When a radio signal is represented digitally, it is made up of a series of samples. One of the widely used sampling methods is I / Q sampling, where each sample is represented by an I component (phase component) and a Q component (quadrature component).

One of the digital representations of an I / Q signal supported by the main tools is to have a succession of samples, with each I and Q component of each sample represented by a floating number between 0 and 1, out of 32 bits, in little-endian mode.

The diagram below shows this format:

+-------+-------+-------+-------+-------+-------+       +-------+-------+
|  i_0  |  q_0  |  i_1  |  q_1  |  i_2  |  q_2  |  ...  |  i_n  |  q_n  |
| (f32) | (f32) | (f32) | (f32) | (f32) | (f32) |  ...  | (f32) | (f32) |
+-------+-------+-------+-------+-------+-------+       +-------+-------+

The challenge.iq file contains a signal represented in the form described above. You need to separate the I and Q components and calculate the resulting SHA256 hash:

hash = SHA256(i_0 | i_1 | ... | i_n | q_0 | q_1 | ... | q_n)
flag = FCSC{<hash>}

SHA256(challenge.iq) = fe4ea6b35841a0107555f1eb18c9f2fbcdef848116750040c2a80c384e6be932.

Files :

Solving the challenge

As explained in the challenge, the IQ file format is a series of 32-bit pairs of values (I, Q). We must read the challenge.iq file so as to concatenate all the phase values then all the quadrature values in succession (without alternating I,Q values).

To do this, we’ll write a python script to read the I, Q values from the file and add them to separate lists as we go. We read each time an unsigned 32-bit integer, represented by the format I in the python library struct (https://docs.python.org/3/library/struct.html#format-characters) :

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import struct
import hashlib

struct_fmt = 'I' # Format of an unsigned int
struct_len = struct.calcsize(struct_fmt)
struct_unpack = struct.Struct(struct_fmt).unpack_from

results = {'I':[],'Q':[]}
with open('challenge.iq', "rb") as f:
    while True:
        data = f.read(struct_len)
        if not data: break
        results['I'].append((data,struct.Struct(struct_fmt).unpack_from(data)))
        data = f.read(struct_len)
        if not data: break
        results['Q'].append((data,struct.Struct(struct_fmt).unpack_from(data)))

rawdata = b''.join([e[0] for e in results['I']] + [e[0] for e in results['Q']])
hash = hashlib.sha256(rawdata).hexdigest()
print("FCSC{%s}" % hash)

Nous n’avons plus qu’à lancer le script:

$ ./solve.py
FCSC{843161934a8e53da8723047bed55e604e725160b868abb74612e243af94345d7}

And we get the flag:

FCSC{843161934a8e53da8723047bed55e604e725160b868abb74612e243af94345d7}