This repository contains the implementation of a secure Satellite TV System for the MITRE eCTF 2025 competition. The system consists of three main components: an Encoder, a Satellite, and a Decoder. The Encoder encodes and encrypts TV frames, the Satellite broadcasts the encoded frames, and the Decoder decrypts and decodes the frames for viewing on authorized TVs. This README will explain how to setup and use the properties of this system with some sample code.
If you wish to gain a deeper understanding of our system here is a link to an AI generated descriprion (via deepwiki) where you can ask questions for clarification:
-
decoder/- Firmware for the television decoderproject.mk- Project-specific variables for the MakefileMakefile- Invoked by eCTF tools when creating a decoderDockerfile- Build environment descriptioninc/- C header filessrc/- C source filesscripts/- Python scripts for decoder key accesswolfssl/- WolfSSL cryptographic library
-
design/- Host design elementsectf25_design/- Host design source codeencoder.py- Frame encoding implementationgen_secrets.py- Shared secrets generationgen_subscription.py- Subscription generation
pyproject.toml- Python package configuration
-
frames/- Example frame data -
tools/- Host tools (DO NOT MODIFY)ectf25/- Tool source codetv/- TV-related functionalityuplink/- Uplink functionalityutils/- Utility functionssatellite.py- Satellite functionality
pyproject.toml- Python package configuration
Our system uses Docker for building components and Python for host tools. Follow these steps to set up your environment:
# Create a virtual environment
cd <repository_root>
python -m venv .venv --prompt FIU-ectf
# Enable virtual environment
source ./.venv/bin/activate
# Install host tools
python -m pip install ./tools/
# Install host design elements as an editable module
python -m pip install -e ./design/# Create a virtual environment
cd <repository_root>
python -m venv .venv --prompt ectf-example
# Enable virtual environment
.\.venv\Scripts\Activate.ps1
# Install host tools
python -m pip install .\tools\
# Install host design elements as an editable module
python -m pip install -e .\design\First, generate the shared secrets used by the decoder and encoder:
mkdir secrets
python -m ectf25_design.gen_secrets secrets/secrets.json 1 3 4This will generate a secrets file for channels 1, 3, and 4.
Next, build the decoder with a specific device ID:
cd <repository_root>/decoder
docker build -t decoder .
docker run --rm -v ./build_out:/out -v ./:/decoder -v ./../secrets:/secrets -e DECODER_ID=0xdeadbeef decodercd <repository_root>\decoder
docker build -t decoder .
docker run --rm -v .\build_out:/out -v .\:/decoder -v .\..\secrets:/secrets -e DECODER_ID=0xdeadbeef decoderNote: If the build hangs indefinitely, try restarting Docker. If that doesn't help, restart your system.
Use gen_subscription.py to generate subscription updates for decoders:
python -m ectf25_design.gen_subscription secrets/secrets.json subscription.bin 0xDEADBEEF 32 128 1This creates a subscription file targeting a device with ID 0xDEADBEEF, with a subscription window from timestamp 32 to 128 for channel 1.
python -m ectf25_design.gen_subscription -h
usage: gen_subscription.py [-h] [--force] secrets_file subscription_file device_id start end channel
positional arguments:
secrets_file Path to the secrets file created by ectf25_design.gen_secrets
subscription_file Subscription output
device_id Device ID of the update recipient.
start Subscription start timestamp
end Subscription end timestamp
channel Channel to subscribe to
options:
-h, --help show this help message and exit
--force, -f Force creation of subscription file, overwriting existing file
Flash the built firmware to your MAX78000 hardware. The device must be in update mode (flashing blue LED):
python -m ectf25.utils.flash ./decoder/build_out/max78000.bin /dev/tty.usbmodem11302python -m ectf25.utils.flash .\decoder\build_out\max78000.bin COM12python -m ectf25.utils.flash -h
usage: ectf25.utils.flash [-h] infile port
positional arguments:
infile Path to the input binary
port Serial port
options:
-h, --help show this help message and exit
NOTE All arguments for these tools can be found within their files, within a function titled "parse_args()." You can also type the command with a -h at the end to get get a prompt with all arguments in tyour temrminal.
View the channels currently subscribed on a decoder:
# Linux
python -m ectf25.tv.list /dev/tty.usbmodem11302
# PowerShell
python -m ectf25.tv.list COM12python -m ectf25.tv.list -h
usage: ectf25.tv.list [-h] port
positional arguments:
port Serial port to the Decoder (see https://rules.ectf.mitre.org/2025/getting_started/boot_reference for platform-specific instructions)
options:
-h, --help show this help message and exit
Update a decoder's subscriptions:
# Linux
python -m ectf25.tv.subscribe subscription.bin /dev/tty.usbmodem11302
# PowerShell
python -m ectf25.tv.subscribe subscription.bin COM12This will subscribe the decoder on connected to specified port to the subscription written to subscription.bin
python -m ectf25.tv.subscribe -h
usage: ectf25.tv.subscribe [-h] subscription_file port
Updates a Decoder's subscription.
positional arguments:
subscription_file Path to the subscription file created by ectf25_design.gen_subscription
port Serial port to the Decoder (see https://rules.ectf.mitre.org/2025/getting_started/boot_reference for platform-specific instructions)
options:
-h, --help show this help message and exit
These are host tools that have been developed to help developers of this system optimize their designs. Use at your own free will.
Test frame decoding functionality:
# Linux
python -m ectf25.utils.tester --port /dev/tty.usbmodem11302 -s secrets/secrets.json rand -c 1 -f 64
# PowerShell
python -m ectf25.utils.tester --port COM12 -s secrets\secrets.json rand -c 1 -f 64This will encode and decode randomly generated 64-byte frames on channel 1.
python -m ectf25.utils.tester -h
usage: ectf25.dev.tester [-h] [--secrets SECRETS] [--port PORT] [--delay DELAY] [--perf]
[--stub-encoder] [--stub-decoder] [--dump-raw DUMP_RAW]
[--dump-encoded DUMP_ENCODED] [--dump-decoded DUMP_DECODED]
{stdin,rand,json} ...
positional arguments:
{stdin,rand,json}
stdin Read frames from stdin
rand Generate random frames
json Read frames from a json file like [[channel, frame, timestamp], ...]
options:
-h, --help show this help message and exit
--secrets SECRETS, -s SECRETS
Path to the secrets file
--port PORT, -p PORT Serial port to the Decoder (See https://rules.ectf.mitre.org/2025/getting_started/boot_reference for platform-specific instructions)
--delay DELAY, -d DELAY
Delay after frame decoding
--perf Display performance stats
--stub-encoder Stub out encoder and pass frames directly to decoder
--stub-decoder Stub out decoder and print decoded frames
--dump-raw DUMP_RAW Dump raw frames to a file
--dump-encoded DUMP_ENCODED
Dump encoded frames to a file
--dump-decoded DUMP_DECODED
Dump decoded frames to a file
Test frame encoding and decoding functionality w/ performance statistics (i.e. FPS, timing fails, failure rate, total decodes) through a single channel, in which a subscription is provided:
# Powershell
python -m ectf25.utils.stability_test -p COM12 -g secrets\secrets.json -c 1 -di 0xdeadbeef -r stab_test.log -d 240 -s This will encoded random frames (using secrets from secrets.json) on channel 1, for a device named 0xdeadbeef connected to port 12, for 240 minutes (4 hours), and a subscription to channel 1 is provided.
python -m ectf25.stability_test -h
usage: ectf25.utils.stability_test [-h] --port PORT --global-secrets SECRETS --channel CHANNEL --decoder-id DECODER ID
--results-file FILENAME --duration MINUTES --subscribe
positional arguments:
-p PORT, --port PORT Decoder serial port
-g, --global-secrets Path to global secrets
-c, --channel Channel to test on
-di, --decoder-id Decoder ID
-r, --results-file File where results will be stored
-d, --duration Duration of the test in minutes
-s, --subscribe Provide a subscription (to chosen channel)
options:
-h, --help show this help message and exit
Test encoder and decoder seperately. The tool generates frames for decoder to encode and dumps these frames to a chosen JSON file; The tool decodes encoded frames from any given file (JSON list of base64-encoded frames):
# Powershell
python -m ectf25.utils.stress_test --test-size 10000 encode secrets\secrets.json --dump frames\encoded_frames.jsonThis will encode 10,000 random 64-byte frames (using secrets.json) and dump these encoded frames to encoded_frames.json.
# Powershell
python -m ectf.utils.stress_test decode COM12 frames\encoded_frames.jsonThis will decode all frames from encoded_frames.json at port COM12
python -m ectf25.utils.stress_test -h
usage: python -m ectf25.utils.stress_test [-h] [-f FRAMESIZE] [-t BYTES] [--channels CHANNELS] {encode, decoder}
secrets SECRETS --dump FILENAME port frames
positional arguments:
encode Test the encoder
decode Test the decoder
options:
frames JSON list of base64-encoded frames (can be created by encoder test)
port Serial port to the Decoder
secrets SECRETS Path to the secrets file
--dump FILENAME Filename of the encoded frames
--frame-size FRAME SIZE, -f FRAME SIZE Size of frame
--test-size BYTES, -t BYTES Bytes to process
--channels CHANNELS, -c CHANNELS Channels to randomly chose from (NOTE 0 os broadcast)
-h, --help show this help message and exit
To run the full system, you need to start three components in sequence (in order):
Start the uplink in one terminal:
# Linux/PowerShell
python -m ectf25.uplink secrets/secrets.json localhost 2000 1:10:frames/x_c0.jsonNote: This uses one of the sample frame files, and localhost must be used as host name if your are running this locally
python -m ectf25.uplink -h
usage: __main__.py [-h] secrets host port channels [channels ...]
positional arguments:
secrets Path to the secrets file
host TCP hostname to serve on
port TCP port to serve on
channels List of channel:fps:frames_file pairings (e.g., 1:10:channel1_frames.json
2:20:channel2_frames.json)
options:
-h, --help show this help message and exit
Start the satellite in another terminal:
# Linux/PowerShell
python -m ectf25.satellite localhost 2000 localhost 1:2001python -m ectf25.satellite -h
usage: satellite.py [-h] up_host up_port down_host channels [channels ...]
positional arguments:
up_host Hostname for uplink
up_port Port for uplink
down_host Hostname for downlink
channels List of channel:down_port pairings (e.g., 1:2001 2:2002)
options:
-h, --help show this help message and exit
Start the TV for each decoder:
# Linux
python -m ectf25.tv.run localhost 2001 /dev/tty.usbmodem11302
# PowerShell
python -m ectf25.tv.run localhost 2001 COM12python -m ectf25.tv.run -h
usage: ectf25.tv.run [-h] [--baud BAUD] sat_host sat_port dec_port
positional arguments:
sat_host TCP host of the satellite
sat_port TCP port of the satellite
dec_port Serial port to the Decoder (see https://rules.ectf.mitre.org/2025/getting_started/boot_reference for platform-specific instructions)
options:
-h, --help show this help message and exit
--baud BAUD Baud rate of the serial port
- Build hangs indefinitely: Restart Docker or your system
- Connection issues: Verify your port settings and device connections
- Subscription failures: Check that the device ID and channel numbers match your configuration
- Decoder not responding: Ensure the device is properly flashed and in the correct mode
- Frame decoding issues: Verify that subscriptions are active and that secrets are properly configured

