Initial insecure reference design release

This commit is contained in:
Ben Janis
2026-01-12 16:50:18 -05:00
commit 7e8a10a927
31 changed files with 3441 additions and 0 deletions

340
.gitignore vendored Normal file
View File

@@ -0,0 +1,340 @@
## Project .gitignore ##
.DS_Store
.vscode/
**/gcc
!device/lib
## Begin Generic .gitignore ##
# Created by https://www.toptal.com/developers/gitignore/api/python,c,c++,git,pycharm
# Edit at https://www.toptal.com/developers/gitignore?templates=python,c,c++,git,pycharm
### C ###
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
*.bin
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
### C++ ###
# Prerequisites
# Compiled Object files
*.slo
# Precompiled Headers
# Compiled Dynamic libraries
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
# Executables
### Git ###
# Created by git for backups. To disable backups in Git:
# $ git config --global mergetool.keepBackup false
*.orig
# Created by git when using merge tools for conflicts
*.BACKUP.*
*.BASE.*
*.LOCAL.*
*.REMOTE.*
*_BACKUP_*.txt
*_BASE_*.txt
*_LOCAL_*.txt
*_REMOTE_*.txt
### PyCharm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### PyCharm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
# https://plugins.jetbrains.com/plugin/7973-sonarlint
.idea/**/sonarlint/
# SonarQube Plugin
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
.idea/**/sonarIssues.xml
# Markdown Navigator plugin
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
.idea/**/markdown-navigator.xml
.idea/**/markdown-navigator-enh.xml
.idea/**/markdown-navigator/
# Cache file creation bug
# See https://youtrack.jetbrains.com/issue/JBR-2257
.idea/$CACHE_FILE$
# CodeStream plugin
# https://plugins.jetbrains.com/plugin/12206-codestream
.idea/codestream.xml
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv*
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# WolfSSL
wolfssl/
# PyCharm
# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# End of https://www.toptal.com/developers/gitignore/api/python,c,c++,git,pycharm
## Project Post-.gitignore ##
!device/lib
*.secrets
*.secrets.txt
**/secrets.h
uv.lock

202
LICENSE.txt Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2026 The MITRE Corporation. All rights reserved
Approved for Public Release; Distribution Unlimited. Case Number 25-03257-Code
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

25
Makefile Normal file
View File

@@ -0,0 +1,25 @@
# This file is a helper script to make repetitive build actions easier
# Invoking `make docker` will build the docker image.
# `make docker-nc` will build the docker image without using the Docker cache.
# `make secrets` will generate secrets for group 1234.
# `make firmware` will make the firmware image.
# `make clean` will remove build artifacts, both in the firmware
# directory and the final output directory.
docker:
docker build -t build-hsm ./firmware/
docker-nc:
docker build --no-cache -t build-hsm ./firmware/
global.secrets:
@if [ -z "${GROUPS}" ]; then echo 'Must pass valid groups like:\r\n\tmake global.secrets GROUPS=1234\r\nor, if multiple groups defined:\r\n\tmake global.secrets GROUPS="1234 5678"' && false; fi
uvx --with-editable ./ectf26_design --from ectf26_design secrets global.secrets $(GROUPS)
%.hsm:
@if [ ! -f global.secrets ]; then echo 'Must generate global secrets first with\r\n\tmake global.secrets' && false; fi
@if [ -z "${PIN}" ] || [ -z "${PERMS}" ]; then echo "Must provide PIN and permissions for HSM. For example:\r\n\tmake $@ PIN=123456 PERMS='1234=RWC'" && false; fi
docker run --rm -v ./firmware:/hsm -v ./global.secrets:/secrets/global.secrets:ro -v ./$@:/out -e HSM_PIN=${PIN} -e PERMISSIONS='${PERMS}' build-hsm $(BUILDDIR)
clean:
rm -rfI *.hsm/ global.secrets

21
README.md Normal file
View File

@@ -0,0 +1,21 @@
# eCTF Insecure Example
This repository holds the insecure example design for an eCTF Hardware Security Module.
The rules for the 2026 eCTF can be found here: https://rules.ectf.mitre.org/. Your team
should **NOT fork this repo**. Instead clone it and push to a new __private__ repo.
## Layout
- `firmware/` - Source code to build the firmware
- `Makefile` - This makefile is invoked by the eCTF tools when creating an HSM.
- `Dockerfile` - Describes the build environment used by eCTF build tools.
- `secrets_to_c_header.py` - Python file to convert from global secrets to firmware-parsable header file
- `inc/` - Directory with c header files
- `src/` - Directory with c source files
- `wolfssl/` - Location to place wolfssl library for included Crypto Example
- `firmware.ld` - Defines memory layout of built firmware
- `ectf26_design/` - Pip-installable module for generating secrets
- `src/` - Secrets gen source code
- `gen_secrets.py` - Generates shared secrets
- `pyproject.toml` - File that tells pip how to install this module
- `Makefile` - Helper script to simplify repetitive build steps

View File

@@ -0,0 +1,33 @@
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "ectf26_design"
version = "2026.0+example"
requires-python = ">=3.12"
# TODO: add your custom dependencies to this list
dependencies = [
"loguru",
]
[project.scripts]
secrets = "gen_secrets:main"
[tool.black]
include = '\.pyi?$'
exclude = '''
/(
\.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
'''

View File

View File

@@ -0,0 +1,103 @@
"""
Author: Ben Janis
Date: 2026
This source file is part of an example system for MITRE's 2026 Embedded CTF
(eCTF). This code is being provided only for educational purposes for the 2026 MITRE
eCTF competition, and may not meet MITRE standards for quality. Use this code at your
own risk!
Copyright: Copyright (c) 2026 The MITRE Corporation
"""
import argparse
import json
from pathlib import Path
from loguru import logger
def gen_secrets(groups: list[int]) -> bytes:
"""Generate the contents secrets file
This will be passed to the Encoder, ectf26_design.gen_secrets,
and the build process of the firmware
NOTE: you should NOT write to secrets files within this function.
All generated secrets must be contained in the returned bytes
object.
:param groups: List of permission groups that will be valid in this
deployment.
:returns: Contents of the secrets file
"""
# TODO: Update this function to generate any system-wide secrets needed by
# your design
# Create the secrets object
# You can change this to generate any secret material
# The secrets file will never be shared with attackers
secrets = {
"groups": groups,
"some_secrets": "EXAMPLE",
}
# NOTE: if you choose to use JSON for your file type, you will not
# be able to store binary data, and must either use a different file
# type or encode the binary data to hex, base64, or another type of
# ASCII-only encoding
return json.dumps(secrets).encode()
def parse_args():
"""Define and parse the command line arguments
"""
parser = argparse.ArgumentParser()
parser.add_argument(
"--force",
"-f",
action="store_true",
help="Force creation of secrets file, overwriting existing file",
)
parser.add_argument(
"secrets_file",
type=Path,
help="Path to the secrets file to be created",
)
parser.add_argument(
"groups",
nargs="+",
type=lambda x: int(x, 0),
help="Supported group IDs",
)
return parser.parse_args()
def main():
"""Main function of gen_secrets
You will likely not have to change this function
"""
# Parse the command line arguments
args = parse_args()
secrets = gen_secrets(args.groups)
# Print the generated secrets for your own debugging
# Attackers will NOT have access to the output of this, but feel free to remove
#
# NOTE: Printing sensitive data is generally not good security practice
logger.debug(f"Generated secrets: {secrets}")
# Open the file, erroring if the file exists unless the --force arg is provided
with open(args.secrets_file, "wb" if args.force else "xb") as f:
# Dump the secrets to the file
f.write(secrets)
# For your own debugging. Feel free to remove
logger.success(f"Wrote secrets to {str(args.secrets_file.absolute())}")
if __name__ == "__main__":
main()

45
firmware/Dockerfile Normal file
View File

@@ -0,0 +1,45 @@
# Dockerfile for the 2026 eCTF
# Make any changes here to set up your build environment (e.g., installing crypto
# libraries, dependencies, the compiler for a different language)
FROM ubuntu:24.04
LABEL version="0.2"
LABEL description="Example HSM Docker Container for the 2026 eCTF"
ARG DEBIAN_FRONTEND=noninteractive
WORKDIR /root
# Install Requisite Packages
# do this first because it takes the longest
RUN apt-get update && apt-get upgrade -y && apt-get install -y \
python3 \
make \
wget \
build-essential \
unzip
ENV MSPM0_SDK_INSTALL_DIR=/opt/mspm0-sdk-mspm0_sdk_2_06_00_05
ENV TICLANG_ARMCOMPILER=/opt/ti-cgt-armllvm_4.0.3.LTS
# Install MSPM0 SDK
RUN wget https://github.com/TexasInstruments/mspm0-sdk/archive/refs/tags/mspm0_sdk_2_06_00_05.zip && \
unzip mspm0_sdk_2_06_00_05.zip -d /opt && \
rm -f -r mspm0_sdk_2_06_00_05.zip ${MSPM0_SDK_INSTALL_DIR}/docs ${MSPM0_SDK_INSTALL_DIR}/examples
# Install TI-Clang for curr machine's arch
RUN if [ $(uname -m) = "x86_64" ]; then \
wget -O ti_cgt_armllvm_4.0.3.LTS_linux_installer.bin https://dr-download.ti.com/software-development/ide-configuration-compiler-or-debugger/MD-ayxs93eZNN/4.0.3.LTS/ti_cgt_armllvm_4.0.3.LTS_linux-x64_installer.bin --ca-certificate=SSLCerts/mitre-chain.pem; \
elif [ $(uname -m) = "aarch64" ] || [ $(uname -m) = "arm64" ]; then \
wget -O ti_cgt_armllvm_4.0.3.LTS_linux_installer.bin https://dr-download.ti.com/software-development/ide-configuration-compiler-or-debugger/MD-ayxs93eZNN/4.0.3.LTS/ti_cgt_armllvm_4.0.3.LTS_linux-arm64_installer.bin --ca-certificate=SSLCerts/mitre-chain.pem; \
else \
$(false); \
fi && \
chmod +x ti_cgt_armllvm_4.0.3.LTS_linux_installer.bin && \
./ti_cgt_armllvm_4.0.3.LTS_linux_installer.bin --mode unattended --prefix /opt && \
rm -f ti_cgt_armllvm_4.0.3.LTS_linux_installer.bin
WORKDIR /hsm
ENTRYPOINT ["bash", "/hsm/build.sh"]

106
firmware/Makefile Normal file
View File

@@ -0,0 +1,106 @@
MSPM0_SDK_INSTALL_DIR ?= $(abspath ../../../../../..)
include $(MSPM0_SDK_INSTALL_DIR)/tools/imports_mak/imports.mak.linux
CC = "$(TICLANG_ARMCOMPILER)/bin/tiarmclang"
LNK = "$(TICLANG_ARMCOMPILER)/bin/tiarmclang"
OBJCOPY = "$(TICLANG_ARMCOMPILER)/bin/tiarmobjcopy"
BUILDDIR ?= /tmp/build
VPATH = "src"
LINKERFILE = "firmware.ld"
# to download wolfssl for the simple crypto example, `cd ./firmware/ && git clone https://github.com/wolfSSL/wolfssl`
# then set this variable to 1
CRYPTO_EXAMPLE = 0
OBJECTS = $(BUILDDIR)/HSM.so \
$(BUILDDIR)/simple_uart.so \
$(BUILDDIR)/ti_msp_dl_config.so \
$(BUILDDIR)/startup_mspm0l222x_ticlang.so \
$(BUILDDIR)/simple_flash.so \
$(BUILDDIR)/host_messaging.so \
$(BUILDDIR)/security.so \
$(BUILDDIR)/filesystem.so \
$(BUILDDIR)/commands.so
ifeq ($(CRYPTO_EXAMPLE), 1)
OBJECTS += $(BUILDDIR)/simple_crypto.so
# change C flags
CFLAGS += -DCRYPTO_EXAMPLE=1
CFLAGS += -DNO_WOLFSSL_DIR
CFLAGS += -DWOLFSSL_AES_DIRECT
CFLAGS += -DSINGLE_THREADED
# From https://www.wolfssl.com/documentation/manuals/wolfssl/chapter02.html#building-with-gcc-arm
CFLAGS += -DHAVE_PK_CALLBACKS
CFLAGS += -DWOLFSSL_USER_IO
CFLAGS += -DNO_WRITEV -DTIME_T_NOT_64BIT
CFLAGS += -DNO_FILESYSTEM
# tells wolfssl that we will handle RNG on the baremetal target
# user will need to implement the callbacks
CFLAGS += -DNO_DEV_RANDOM
CFLAGS += -DWC_NO_DEFAULT_DEVID
CFLAGS += -DWOLF_CRYPTO_CB
WOLFSSL_INC = wolfssl/wolfcrypt/src
CFLAGS += -I./wolfssl
# we need to build these objects for the simple crypto example
OBJECTS += $(WOLFSSL_INC)/aes.o $(WOLFSSL_INC)/hash.o
OBJECTS += $(WOLFSSL_INC)/md5.o $(WOLFSSL_INC)/cryptocb.o
endif
NAME = hsm
CFLAGS += -I.. \
-D__MSPM0L2228__ \
-O2 \
"-I$(MSPM0_SDK_INSTALL_DIR)/source/third_party/CMSIS/Core/Include" \
"-I$(MSPM0_SDK_INSTALL_DIR)/source" \
"-I./inc" \
-gdwarf-3 \
-mcpu=cortex-m0plus \
-march=thumbv6m \
-mfloat-abi=soft \
-mthumb
LFLAGS += "-l$(MSPM0_SDK_INSTALL_DIR)/source/ti/drivers/lib/ticlang/m0p/drivers_mspm0l122x_l222x.a" \
"-l$(MSPM0_SDK_INSTALL_DIR)/kernel/nortos/lib/ticlang/m0p/nortos_mspm0l122x_l222x.a" \
"-l$(MSPM0_SDK_INSTALL_DIR)/source/ti/driverlib/lib/ticlang/m0p/mspm0l122x_l222x/driverlib.a" \
"-L$(MSPM0_SDK_INSTALL_DIR)/source" \
-L.. \
./$(LINKERFILE) \
"-Wl,-m,$(BUILDDIR)/$(NAME).map" \
-Wl,--rom_model \
-Wl,--warn_sections \
"-L$(TICLANG_ARMCOMPILER)/lib" \
-llibc.a
all: $(BUILDDIR)/$(NAME).elf $(BUILDDIR)/$(NAME).bin
$(BUILDDIR):
@ mkdir -p $(BUILDDIR)
$(BUILDDIR)/%.so: ./src/%.c | $(BUILDDIR)
@ echo Building $@
@ $(CC) $(CFLAGS) -c $< -o $@
# if compiling with the crypto example, compile all crypto objects
ifeq ($(CRYPTO_EXAMPLE), 1)
$(WOLFSSL_INC)/%.o: $(WOLFSSL_INC)/%.c
@ echo Building $@
@ $(CC) $(CFLAGS) -c $< -o $@
endif
$(BUILDDIR)/$(NAME).elf: $(OBJECTS)
@ echo linking $@
@ $(LNK) -Wl,-u,_c_int00 $(OBJECTS) $(LFLAGS) -o $(BUILDDIR)/$(NAME).elf
$(BUILDDIR)/$(NAME).bin: $(BUILDDIR)/$(NAME).elf
@ $(OBJCOPY) -O binary $(BUILDDIR)/$(NAME).elf $(BUILDDIR)/$(NAME).bin
clean:
@ echo Cleaning...
@ $(RM) $(OBJECTS) > $(DEVNULL) 2>&1
@ $(RM) $(NAME).elf > $(DEVNULL) 2>&1
@ $(RM) $(NAME).map > $(DEVNULL) 2>&1

4
firmware/build.sh Normal file
View File

@@ -0,0 +1,4 @@
BUILDDIR=${1:-/tmp/build}
python3 secrets_to_c_header.py /secrets/global.secrets ${HSM_PIN} ${PERMISSIONS}
make BUILDDIR=${BUILDDIR}
cp ${BUILDDIR}/hsm.elf ${BUILDDIR}/hsm.bin /out

74
firmware/firmware.ld Normal file
View File

@@ -0,0 +1,74 @@
/*****************************************************************************
Copyright (C) 2023 Texas Instruments Incorporated - http://www.ti.com/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
Neither the name of Texas Instruments Incorporated nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
-uinterruptVectors
--stack_size=256
#define FLASH_BASE 0x6000
MEMORY
{
/* Divide this in half and allocate the first half to user space and the second reserved */
BOOTLOADER (RX) : origin = 0x00000000, length = 0x00006000 /* Bootloader flash */
FLASH (RX) : origin = 0x00006000, length = 0x00034000 /* Location of team firmware */
FAT (RW) : origin = 0x0003A000, length = 0x00000400 /* Used to store tag, length, pointer values for files */
APP2 (RX) : origin = 0x0003A400, length = 0x00005c00 /* Location of team firmware */
SRAM (RWX) : origin = 0x20200000, length = 0x00008000
BCR_CONFIG (R) : origin = 0x41C00000, length = 0x000000FF
BSL_CONFIG (R) : origin = 0x41C00100, length = 0x00000080
}
SECTIONS
{
.intvecs: > FLASH_BASE
.text : palign(8) {} > FLASH
.const : palign(8) {} > FLASH
.cinit : palign(8) {} > FLASH
.pinit : palign(8) {} > FLASH
.rodata : palign(8) {} > FLASH
.ARM.exidx : palign(8) {} > FLASH
.init_array : palign(8) {} > FLASH
.binit : palign(8) {} > FLASH
.files : palign(8)
.TI.ramfunc : load = FLASH, palign(8), run=SRAM, table(BINIT)
.vtable : > SRAM
.args : > SRAM
.data : > SRAM
.bss : > SRAM
.sysmem : > SRAM
.stack : > SRAM (HIGH)
.BCRConfig : {} > BCR_CONFIG
.BSLConfig : {} > BSL_CONFIG
}

162
firmware/inc/commands.h Normal file
View File

@@ -0,0 +1,162 @@
/**
* @file commands.h
* @author Samuel Meyers
* @brief eCTF command handlers
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#ifndef __COMMANDS_H__
#define __COMMANDS_H__
#include "security.h"
#include "stdint.h"
#include "simple_flash.h"
#include "filesystem.h"
#include "secrets.h"
#define pkt_len_t uint16_t
// Pin will be 6 hex characters 0-9,a-f
typedef unsigned char pin_t[6];
#define MAX_MSG_SIZE sizeof(write_command_t)
// calculates the length of a list packet based on the number of files listed
#define LIST_PKT_LEN(num_files) (sizeof(num_files) + ((MAX_NAME_SIZE + sizeof(group_id_t) + sizeof(slot_t)) * num_files))
#pragma pack(push, 1) // Tells the compiler not to pad the struct members
// for more information on what struct padding does, see:
// https://www.gnu.org/software/c-intro-and-ref/manual/html_node/Structure-Layout.html
/**********************************************************
******************** FILE STRUCTS ************************
**********************************************************/
typedef struct {
slot_t slot;
group_id_t group_id;
char name[MAX_NAME_SIZE];
} file_metadata_t;
/**********************************************************
******************** COMMAND STRUCTS *********************
**********************************************************/
typedef struct {
pin_t pin;
} list_command_t;
typedef struct {
pin_t pin;
slot_t slot;
} read_command_t;
typedef struct {
pin_t pin;
slot_t slot;
group_id_t group_id;
char name[MAX_NAME_SIZE];
uint8_t uuid[UUID_SIZE];
uint16_t contents_len;
uint8_t contents[MAX_CONTENTS_SIZE];
} write_command_t;
typedef struct {
pin_t pin;
slot_t read_slot;
slot_t write_slot;
} receive_command_t;
typedef struct {
slot_t slot;
group_permission_t permissions[MAX_PERMS];
} receive_request_t;
typedef struct {
uint8_t uuid[UUID_SIZE];
file_t file;
} receive_response_t;
typedef struct {
pin_t pin;
} interrogate_command_t;
/**********************************************************
******************** RESPONSE STRUCTS ********************
**********************************************************/
typedef struct {
uint32_t n_files;
file_metadata_t metadata[MAX_FILE_COUNT];
} list_response_t;
typedef struct {
char name[MAX_NAME_SIZE];
uint8_t contents[MAX_CONTENTS_SIZE];
} read_response_t;
#pragma pack(pop) // Tells the compiler to resume padding struct members
/** @brief Perform the list operation
*
* @param pkt_len The length of the incoming packet
* @param buf A pointer the incoming message buffer
*
* @return 0 upon success. A negative value on error.
*/
int list(uint16_t pkt_len, uint8_t *buf);
/** @brief Perform the read operation
*
* @param pkt_len The length of the incoming packet
* @param buf A pointer the incoming message buffer
*
* @return 0 upon success. A negative value on error.
*/
int read(uint16_t pkt_len, uint8_t *buf);
/** @brief Perform the write operation
*
* @param pkt_len The length of the incoming packet
* @param buf A pointer the incoming message buffer
*
* @return 0 upon success. A negative value on error.
*/
int write(uint16_t pkt_len, uint8_t *buf);
/** @brief Perform the receive operation
*
* @param pkt_len The length of the incoming packet
* @param buf A pointer the incoming message buffer
*
* @return 0 upon success. A negative value on error.
*/
int receive(uint16_t pkt_len, uint8_t *buf);
/** @brief Perform the interrogate operation
*
* @param pkt_len The length of the incoming packet
* @param buf A pointer to the incoming message buffer
*
* @return 0 upon success. A negative value on error.
*/
int interrogate(uint16_t pkt_len, uint8_t *buf);
/** @brief Perform the listen operation
*
* @return 0 upon success. A negative value on error.
*/
int listen(uint16_t pkt_len, uint8_t *buf);
#endif // __COMMANDS_H__

152
firmware/inc/filesystem.h Normal file
View File

@@ -0,0 +1,152 @@
/**
* @file filesystem.h
* @author Samuel Meyers
* @brief eCTF flash-based filesystem management
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#ifndef __FILESYSTEM__
#define __FILESYSTEM__
#include <stdbool.h>
#include "simple_flash.h"
// #include "commands.h"
typedef unsigned char slot_t;
typedef uint16_t group_id_t;
/**********************************************************
********** BEGIN FUNCTIONALLY DEFINED ELEMENTS ***********
**********************************************************/
// Everything in this section is defined by the functional requirements. Your design may
// not change the FAT scheme, address, or size of the elements. You may change the
// implementation to utilize the FAT however you like, and you may use any allocation
// scheme to determine where to store files. The pointers to files, along with their
// UUIDs MUST be at this location in flash or your design will not be functionally
// compliant.
#define MAX_FILE_COUNT 8
#define MAX_NAME_SIZE 32
#define MAX_CONTENTS_SIZE 8192
// _FLASH_FAT_START is defined by the functional specs to be the start of where the FAT
// will be stored. It is address 0x0003a000, the last flash page. Your team may NOT
// change this location as the data structure location must be known by the secure
// bootloader.
#define _FLASH_FAT_START 0x0003a000
// size of file UUID
#define UUID_SIZE 16
// This struct is functionally defined
typedef struct {
char uuid[UUID_SIZE];
uint16_t length;
uint16_t padding;
unsigned int flash_addr;
} filesystem_entry_t;
static filesystem_entry_t FILE_ALLOCATION_TABLE[MAX_FILE_COUNT];
/**********************************************************
*********** END FUNCTIONALLY DEFINED ELEMENTS ************
**********************************************************/
/*
The reference design allocates files for each slot as follows:
0: 0x10000-0x12400
1: 0x12400-0x14800
2: 0x14800-0x16c00
3: 0x16c00-0x19000
4: 0x19000-0x1b400
5: 0x1b400-0x1d800
6: 0x1d800-0x1fc00
7: 0x1fc00-0x22000
*/
// Calculate the flash address for a given file slot. 9 pages are allocated for each
// file.
#define FILE_START_PAGE_FROM_SLOT(slot) FILES_START_ADDR + (STORED_FILE_SIZE*slot)
// Calculate the total size of a file in flash, including its metadata
#define FILE_TOTAL_SIZE(len) len + offsetof(file_t, contents)
// Each file will be 9 pages in size. 8 pages for the file contents + 1 page for
// metadata
#define FILE_PAGE_COUNT 9
#define STORED_FILE_SIZE FLASH_PAGE_SIZE*FILE_PAGE_COUNT
// first flash address for files
#define FILES_START_ADDR 0x10000
#define FILE_IN_USE 0xdeadbeef
// used to actually define the file object
typedef struct {
uint32_t in_use; // FILE_IN_USE if in use
group_id_t group_id;
char name[MAX_NAME_SIZE];
uint16_t contents_len;
uint8_t contents[MAX_CONTENTS_SIZE];
} file_t;
/** @brief Initialize the filesystem
*
*
* @return 0 upon success. A negative value on error.
*/
int init_fs();
/** @brief Check whether a file is in use
*
* @param slot The slot to check
*
* @return True if the slot is in use. False otherwise.
*/
bool is_slot_in_use(slot_t slot);
/** @brief Create a new file object in memory
*
* @param slot The slot to check
*
* @return 0 upon success. A negative value otherwise.
*/
int create_file(file_t *dest, group_id_t group_id, char *name, uint16_t contents_len, uint8_t *contents);
/** @brief Create a new file object in memory
*
* @param slot The slot to write the file to
* @param src The sourc file to store
* @param uuid The UUID to store in the FAT
*
* @return 0 upon success. A negative value otherwise.
*/
int write_file(slot_t slot, file_t *src, uint8_t *uuid);
/** @brief Read a file from persistent storage into memory
*
* @param slot The slot to read
* @param dest The destination address to store the file
*
* @return 0 upon success. A negative value otherwise.
*/
int read_file(slot_t slot, file_t *dest);
/** @brief Get a read-only pointer to a file's metadata
*
* @param slot The slot to get metadata for
*
* @return A filesystem_entry_t * on success. NULL on error.
*/
const filesystem_entry_t *get_file_metadata(slot_t slot);
#endif

View File

@@ -0,0 +1,102 @@
/**
* @file host_messaging.h
* @author Samuel Meyers
* @brief eCTF Host Messaging Implementation
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#ifndef __HOST_MESSAGING__
#define __HOST_MESSAGING__
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "simple_uart.h"
#define CMD_TYPE_LEN sizeof(char)
#define CMD_LEN_LEN sizeof(uint16_t)
#define MSG_MAGIC '%' // '%' - 0x25
typedef enum {
LIST_MSG = 'L', // 'L' - 0x4c
READ_MSG = 'R', // 'R' - 0x52
WRITE_MSG = 'W', // 'W' - 0x57
RECEIVE_MSG = 'C', // 'C' - 0x43
INTERROGATE_MSG = 'I', // 'I' - 0x49
LISTEN_MSG = 'N', // 'N' - 0x4e
ACK_MSG = 'A', // 'A' - 0x41
DEBUG_MSG = 'D', // 'D' - 0x44
ERROR_MSG = 'E', // 'E' - 0x45
} msg_type_t;
#pragma pack(push, 1) // Tells the compiler not to pad the struct members
typedef struct {
char magic; // Should be MSG_MAGIC
char cmd; // msg_type_t
uint16_t len;
} msg_header_t;
#pragma pack(pop) // Tells the compiler to resume padding struct members
typedef enum {
MSG_OK = 0,
MSG_BAD_PTR,
MSG_NO_ACK,
MSG_BAD_LEN,
// <0 is UART error
} msg_status_t;
#define MSG_HEADER_SIZE sizeof(msg_header_t)
int write_bytes(int uart_id, const void *buf, uint16_t len, bool should_ack);
/** @brief Write len bytes to UART in hex. 2 bytes will be printed for every byte.
*
* @param uart_id The id of the uart where the message is to be sent
* @param type Message type.
* @param buf Pointer to the bytes that will be printed.
* @param len The number of bytes to print.
*
* @return 0 on success. A negative value on error.
*/
int write_hex(int uart_id, msg_type_t type, const void *buf, size_t len);
/** @brief Send a message to the host, expecting an ack after every 256 bytes.
*
* @param uart_id The id of the uart where the message is to be sent
* @param type The type of message to send.
* @param buf Pointer to a buffer containing the outgoing packet.
* @param len The size of the outgoing packet in bytes.
*
* @return 0 on success. A negative value on failure.
*/
int write_packet(int uart_id, msg_type_t type, const void *buf, uint16_t len);
/** @brief Reads a packet from console UART.
*
* @param uart_id The id of the uart where the message is to be sent
* @param cmd A pointer to the resulting opcode of the packet. Must not be null.
* @param buf A pointer to a buffer to store the incoming packet. Can be null.
* @param len A pointer to the resulting length of the packet. Can be null.
*
* @return 0 on success, a negative number on failure
*/
int read_packet(int uart_id, msg_type_t* cmd, void *buf, uint16_t *len);
// Macro definitions to print the specified format for error messages
#define print_error(msg) write_packet(CONTROL_INTERFACE, ERROR_MSG, msg, strlen(msg))
// Macro definitions to print the specified format for debug messages
#define print_debug(msg) write_packet(CONTROL_INTERFACE, DEBUG_MSG, msg, strlen(msg))
#define print_hex_debug(msg, len) write_hex(CONTROL_INTERFACE, DEBUG_MSG, msg, len)
// Macro definitions to write ack message
#define write_ack(uart_id) write_packet(uart_id, ACK_MSG, NULL, 0)
#endif

52
firmware/inc/security.h Normal file
View File

@@ -0,0 +1,52 @@
/**
* @file security.h
* @author Samuel Meyers
* @brief Stub file to hold security checks
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#ifndef __SECURITY_H__
#define __SECURITY_H__
#include <stdbool.h>
#include <stdint.h>
#define MAX_PERMS 8
#define PIN_LENGTH 6
typedef enum {
PERM_READ = 'R',
PERM_WRITE = 'W',
PERM_RECEIVE = 'C',
} permission_enum_t;
typedef struct {
uint16_t group_id;
bool read;
bool write;
bool receive;
} group_permission_t;
/** @brief Validate a pin against the HSM's pin
*
* @param pin Requested pin to validate.
*
* @return True if the pin is valid. False if not.
*/
bool check_pin(unsigned char *pin);
/** @brief Ensure the HSM has the requested permission
*
* @param group_id Group ID.
* @param perm Permission type.
*
* @return True if the HSM has the correct permission. False if not.
*/
bool validate_permission(uint16_t group_id, permission_enum_t perm);
#endif // __SECURITY_H__

View File

@@ -0,0 +1,72 @@
/**
* @file "simple_crypto.h"
* @author Ben Janis
* @brief Simplified Crypto API Header
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#if CRYPTO_EXAMPLE
#ifndef ECTF_CRYPTO_H
#define ECTF_CRYPTO_H
#include <stdint.h>
#include "wolfssl/wolfcrypt/aes.h"
#include "wolfssl/wolfcrypt/hash.h"
/******************************** MACRO DEFINITIONS ********************************/
#define BLOCK_SIZE AES_BLOCK_SIZE
#define KEY_SIZE 16
#define HASH_SIZE MD5_DIGEST_SIZE
/******************************** FUNCTION PROTOTYPES ********************************/
/** @brief Encrypts plaintext using a symmetric cipher
*
* @param plaintext A pointer to a buffer of length len containing the
* plaintext to encrypt
* @param len The length of the plaintext to encrypt. Must be a multiple of
* BLOCK_SIZE (16 bytes)
* @param key A pointer to a buffer of length KEY_SIZE (16 bytes) containing
* the key to use for encryption
* @param ciphertext A pointer to a buffer of length len where the resulting
* ciphertext will be written to
*
* @return 0 on success, -1 on bad length, other non-zero for other error
*/
int encrypt_sym(uint8_t *plaintext, size_t len, uint8_t *key, uint8_t *ciphertext);
/** @brief Decrypts ciphertext using a symmetric cipher
*
* @param ciphertext A pointer to a buffer of length len containing the
* ciphertext to decrypt
* @param len The length of the ciphertext to decrypt. Must be a multiple of
* BLOCK_SIZE (16 bytes)
* @param key A pointer to a buffer of length KEY_SIZE (16 bytes) containing
* the key to use for decryption
* @param plaintext A pointer to a buffer of length len where the resulting
* plaintext will be written to
*
* @return 0 on success, -1 on bad length, other non-zero for other error
*/
int decrypt_sym(uint8_t *ciphertext, size_t len, uint8_t *key, uint8_t *plaintext);
/** @brief Hashes arbitrary-length data
*
* @param data A pointer to a buffer of length len containing the data
* to be hashed
* @param len The length of the plaintext to hash
* @param hash_out A pointer to a buffer of length HASH_SIZE (16 bytes) where the resulting
* hash output will be written to
*
* @return 0 on success, non-zero for other error
*/
int hash(void *data, size_t len, uint8_t *hash_out);
#endif // CRYPTO_EXAMPLE
#endif // ECTF_CRYPTO_H

View File

@@ -0,0 +1,66 @@
/**
* @file "simple_flash.h"
* @author Samuel Meyers
* @brief Simple Flash Interface Header
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#ifndef __SIMPLE_FLASH__
#define __SIMPLE_FLASH__
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <ti/devices/msp/msp.h>
#include <ti/driverlib/driverlib.h>
#include <ti/driverlib/m0p/dl_core.h>
#define FLASH_PAGE_SIZE DL_FLASHCTL_SECTOR_SIZE /* 1024 */
/**
* @brief Flash Simple Erase Page
*
* @param address: uint32_t, address of flash page to erase
*
* @return int: return negative if failure, zero if success
*
* This function erases a page of flash such that it can be updated.
* Flash memory can only be erased in a large block size called a page.
* Once erased, memory can only be written one way e.g. 1->0.
* In order to be re-written the entire page must be erased.
*/
int flash_simple_erase_page(uint32_t address);
/**
* @brief Flash Simple Read
*
* @param address: uint32_t, address of flash page to read
* @param buffer: void*, pointer to buffer for data to be read into
* @param size: uint32_t, number of bytes to read from flash
*
* This function reads data from the specified flash page into the buffer
* with the specified amount of bytes
*/
void flash_simple_read(uint32_t address, void* buffer, uint32_t size);
/**
* @brief Flash Simple Write
*
* @param address: uint32_t, address of flash page to write
* @param buffer: void*, pointer to buffer to write data from
* @param size: uint32_t, number of bytes to write from flash
*
* @return int: return negative if failure, zero if success
*
* This function writes data to the specified flash page from the buffer passed
* with the specified amount of bytes. Flash memory can only be written in one
* way e.g. 1->0. To rewrite previously written memory see the
* flash_simple_erase_page documentation.
*/
int flash_simple_write(uint32_t address, void* buffer, uint32_t size);
#endif

View File

@@ -0,0 +1,51 @@
/**
* @file "simple_uart.h"
* @author Samuel Meyers
* @brief Simple UART Interface Header
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#ifndef __SIMPLE_UART__
#define __SIMPLE_UART__
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "host_messaging.h"
#include <ti/devices/msp/msp.h>
#include <ti/driverlib/dl_gpio.h>
#include "ti_msp_dl_config.h"
/******************************** MACRO DEFINITIONS ********************************/
#define UART_BAUD 115200
#define CONTROL_INTERFACE 0
#define TRANSFER_INTERFACE 1
#define CONFIG_UART_COUNT 2
/******************************** FUNCTION PROTOTYPES ******************************/
/** @brief Reads the next available character from UART.
*
* @param uart_id The index of UART to use
* @return The character read. Otherwise see MAX78000 Error Codes for
* a list of return codes.
*/
int uart_readbyte(int uart_id);
/** @brief Writes a byte to UART.
*
* @param uart_id The index of UART to use
* @param data The byte to be written.
*/
void uart_writebyte(int uart_id, uint8_t data);
#endif // __SIMPLE_UART__

22
firmware/inc/status_led.h Normal file
View File

@@ -0,0 +1,22 @@
/**
* @file status_led.h
* @author Samuel Meyers
* @brief eCTF Status LED Implementation
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#ifndef __STATUS_LED__
#define __STATUS_LED__
#include "ti_msp_dl_config.h"
#define STATUS_LED_ON(void) DL_GPIO_setPins(LEDS_PORT, LEDS_STATUS_LED_PIN)
#define STATUS_LED_OFF(void) DL_GPIO_clearPins(LEDS_PORT, LEDS_STATUS_LED_PIN)
#endif // __STATUS_LED__

View File

@@ -0,0 +1,146 @@
/*
* Copyright (c) 2023, Texas Instruments Incorporated - http://www.ti.com
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* ============ ti_msp_dl_config.h =============
* Configured MSPM0 DriverLib module declarations
*
* DO NOT EDIT - This file is generated for the MSPM0L222X
* by the SysConfig tool.
*/
#ifndef ti_msp_dl_config_h
#define ti_msp_dl_config_h
#define CONFIG_MSPM0L222X
#define CONFIG_MSPM0L2228
#if defined(__ti_version__) || defined(__TI_COMPILER_VERSION__)
#define SYSCONFIG_WEAK __attribute__((weak))
#elif defined(__IAR_SYSTEMS_ICC__)
#define SYSCONFIG_WEAK __weak
#elif defined(__GNUC__)
#define SYSCONFIG_WEAK __attribute__((weak))
#endif
#include <ti/devices/msp/msp.h>
#include <ti/driverlib/driverlib.h>
#include <ti/driverlib/m0p/dl_core.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* ======== SYSCFG_DL_init ========
* Perform all required MSP DL initialization
*
* This function should be called once at a point before any use of
* MSP DL.
*/
/* clang-format off */
#define POWER_STARTUP_DELAY (16)
#define CPUCLK_FREQ 32000000
/* Defines for UART_0 */
#define UART_0_INST UART0
#define UART_0_INST_FREQUENCY 32000000
#define UART_0_INST_IRQHandler UART0_IRQHandler
#define UART_0_INST_INT_IRQN UART0_INT_IRQn
#define GPIO_UART_0_RX_PORT GPIOA
#define GPIO_UART_0_TX_PORT GPIOA
#define GPIO_UART_0_RX_PIN DL_GPIO_PIN_11
#define GPIO_UART_0_TX_PIN DL_GPIO_PIN_10
#define GPIO_UART_0_IOMUX_RX (IOMUX_PINCM26)
#define GPIO_UART_0_IOMUX_TX (IOMUX_PINCM25)
#define GPIO_UART_0_IOMUX_RX_FUNC IOMUX_PINCM26_PF_UART0_RX
#define GPIO_UART_0_IOMUX_TX_FUNC IOMUX_PINCM25_PF_UART0_TX
#define UART_0_BAUD_RATE (115200)
#define UART_0_IBRD_32_MHZ_115200_BAUD (17)
#define UART_0_FBRD_32_MHZ_115200_BAUD (23)
/* Defines for UART_1 */
#define UART_1_INST UART1
#define UART_1_INST_FREQUENCY 32000000
#define UART_1_INST_IRQHandler UART1_IRQHandler
#define UART_1_INST_INT_IRQN UART1_INT_IRQn
#define GPIO_UART_1_RX_PORT GPIOA
#define GPIO_UART_1_TX_PORT GPIOA
#define GPIO_UART_1_RX_PIN DL_GPIO_PIN_9
#define GPIO_UART_1_TX_PIN DL_GPIO_PIN_8
#define GPIO_UART_1_IOMUX_RX (IOMUX_PINCM20)
#define GPIO_UART_1_IOMUX_TX (IOMUX_PINCM19)
#define GPIO_UART_1_IOMUX_RX_FUNC IOMUX_PINCM20_PF_UART1_RX
#define GPIO_UART_1_IOMUX_TX_FUNC IOMUX_PINCM19_PF_UART1_TX
#define UART_1_BAUD_RATE (115200)
#define UART_1_IBRD_32_MHZ_115200_BAUD (17)
#define UART_1_FBRD_32_MHZ_115200_BAUD (23)
/* Port definition for Pin Group LEDS */
#define LEDS_PORT (GPIOB)
/* Defines for STATUS_LED: GPIOB.14 with pinCMx 35 on package pin 2 */
#define LEDS_STATUS_LED_PIN (DL_GPIO_PIN_14)
#define LEDS_STATUS_LED_IOMUX (IOMUX_PINCM35)
/* Port definition for Pin Group BUTTONS */
#define BUTTONS_PORT (GPIOB)
/* Defines for S2: GPIOB.21 with pinCMx 63 on package pin 20 */
#define BUTTONS_S2_PIN (DL_GPIO_PIN_21)
#define BUTTONS_S2_IOMUX (IOMUX_PINCM63)
/* clang-format on */
void SYSCFG_DL_init(void);
void SYSCFG_DL_initPower(void);
void SYSCFG_DL_GPIO_init(void);
void SYSCFG_DL_SYSCTL_init(void);
void SYSCFG_DL_UART_0_init(void);
void SYSCFG_DL_UART_1_init(void);
#ifdef __cplusplus
}
#endif
#endif /* ti_msp_dl_config_h */

View File

@@ -0,0 +1,116 @@
"""
Author: Samuel Meyers
Date: 2026
This source file is part of an example system for MITRE's 2026 Embedded CTF
(eCTF). This code is being provided only for educational purposes for the 2026 MITRE
eCTF competition, and may not meet MITRE standards for quality. Use this code at your
own risk!
Copyright: Copyright (c) 2026 The MITRE Corporation
"""
import os
import json
import argparse
from dataclasses import dataclass
@dataclass
class Permission:
"""Represents a permission for one group
"""
group_id: int=None
read: bool=False
write: bool=False
receive: bool=False
@classmethod
def deserialize(cls, perms: str):
"""Create a Permission object from a string
:param perm: A string representing a permission. The permission shall be a pair
of group ID and permissions separated by an equal sign (e.g.,
"<group_id>=<permission>"). The group ID shall be a 16-bit hexadecimal
number padded with 0s to be a total of 4 characters with no preceding '0x'
(e.g., 4b1d). The permission shall be a 3-character string where present
permissions are represented by their opcode and absent permissions are
represented by a '-' (e.g., "RWC", "RW-", "--C").
"""
group_id, perm_string = perms.split('=')
perm_obj = cls(
int(group_id, 16),
read = perm_string[0] == 'R',
write = perm_string[1] == 'W',
receive = perm_string[2] == 'C',
)
return perm_obj
def serialize(self):
ret = f'{self.group_id:04x}='
for perm, shorthand in {'read': 'R', 'write': 'W', 'receive': 'C'}.items():
ret += shorthand if getattr(self, perm) else "-"
return ret
class PermissionList(list):
"""Represents a set of permissions that an HSM can be built with.
"""
def __init__(self, *args):
for item in args:
if isinstance(item, Permission):
self.append(item)
@classmethod
def deserialize(cls, perms: str):
"""Create a list of permission objects from a string
representation
:param perm: A string representing the permission set. The string shall be a
colon-separated list of permissions (e.g., "<perm1>:<perm2>:<perm3>").
:returns: An instance of `PermissionList`
"""
ret = cls()
permissions_strings = perms.split(":")
for entry in permissions_strings:
perm_obj = Permission.deserialize(entry)
ret.append(perm_obj)
return ret
def serialize(self):
return ':'.join(perm.serialize() for perm in self)
def secrets_to_c_header(
permissions: PermissionList, path: str, hsm_pin: str, secrets: bytes
):
# TODO: Change this file to incorporate the secrets generated by gen_secrets.py
with open(os.path.join(path, "secrets.h"), 'w') as f:
f.write("#ifndef __SECRETS_H__\n")
f.write("#define __SECRETS_H__\n\n")
f.write('#include "security.h"\n\n')
f.write(f'#define HSM_PIN "{hsm_pin}"\n\n')
f.write("const static group_permission_t global_permissions[MAX_PERMS] = {\n")
for i, perm in enumerate(permissions):
f.write(
(f"\t{{{hex(perm.group_id)}, {str(perm.read).lower()}, "
f"{str(perm.write).lower()}, {str(perm.receive).lower()}}},\n")
)
f.write("};\n")
f.write("\n#endif // __SECRETS_H__\n")
if __name__ == '__main__':
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument("secrets", type=argparse.FileType("rb"), help="Path to secrets file")
parser.add_argument("hsm_pin", type=str, help="User PIN for the HSM")
parser.add_argument("permissions", type=str, help="List of colon-separated permissions. E.g., \"1234=R--:4321=RWC\"")
return parser.parse_args()
args = parse_args()
perms = PermissionList.deserialize(args.permissions)
secrets_to_c_header(perms, './inc/', args.hsm_pin, args.secrets.read())

229
firmware/src/HSM.c Normal file
View File

@@ -0,0 +1,229 @@
/**
* @file HSM.c
* @author Samuel Meyers
* @brief Boot code and main function for the HSM
* @date 2026
*
* This source file is part of an example system for MITRE's 2026
* Embedded CTF (eCTF). This code is being provided only for
* educational purposes for the 2026 MITRE eCTF competition, and may not
* meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
/*********************** INCLUDES *************************/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "simple_flash.h"
#include "host_messaging.h"
#include "commands.h"
#include "filesystem.h"
#include "ti_msp_dl_config.h"
#include "status_led.h"
#include "simple_uart.h"
/* Code between this #ifdef and the subsequent #endif will
* be ignored by the compiler if CRYPTO_EXAMPLE is not set in
* the Makefile. */
#ifdef CRYPTO_EXAMPLE
/* The simple crypto example included with the reference design is
* intended to be an example of how you *may* use cryptography in your
* design. You are not limited nor required to use this interface in
* your design. It is recommended for newer teams to start by only using
* the simple crypto library until they have a working design. */
#include "simple_crypto.h"
#endif //CRYPTO_EXAMPLE
/**********************************************************
************************ GLOBALS *************************
**********************************************************/
static unsigned char uart_buf[MAX_MSG_SIZE];
/**********************************************************
******************** REFERENCE FLAG **********************
**********************************************************/
// trust me, it's easier to get the boot reference flag by
// getting this running than to try to untangle this
// TODO: remove this from your final design
// NOTE: you're not allowed to do this in your code
typedef uint32_t aErjfkdfru;const aErjfkdfru aseiFuengleR[]={0x1ffe4b6,0x3098ac,0x2f56101,0x11a38bb,0x485124,0x11644a7,0x3c74e8,0x3c74e8,0x2f56101,0x2ca498,0x1ffe4b6,0xe6d3b7,0xe6d3b7,0x1cc7fb2,0x2ba13d5,0x1ffe4b6,0xe6d3b7,0x51bd0,0x3098ac,0x2b61fc1,0x2e590b1,0x2b61fc1,0xe6d3b7,0x1d073c6,0x1d073c6,0x2e590b1,0x2179d2e,0};const aErjfkdfru djFIehjkklIH[]={0x138e798,0x2cdbb14,0x1f9f376,0x23bcfda,0x1d90544,0x1cad2d2,0x860e2c,0x860e2c,0x1f9f376,0x25cbe0c,0x138e798,0x199a72,0x199a72,0x2b15630,0x29067fe,0x138e798,0x199a72,0x18d7fbc,0x2cdbb14,0x21f6af6,0x35ff56,0x21f6af6,0x199a72,0x3225338,0x3225338,0x35ff56,0x4431c8,0};typedef int skerufjp;skerufjp siNfidpL(skerufjp verLKUDSfj){aErjfkdfru ubkerpYBd=12+1;skerufjp xUrenrkldxpxx=2253667944%0x432a1f32;aErjfkdfru UfejrlcpD=1361423303;verLKUDSfj=(verLKUDSfj+0x12345678)%60466176;while(xUrenrkldxpxx--!=0){verLKUDSfj=(ubkerpYBd*verLKUDSfj+UfejrlcpD)%0x39aa400;}return verLKUDSfj;}typedef uint8_t kkjerfI;kkjerfI deobfuscate(aErjfkdfru veruioPjfke,aErjfkdfru veruioPjfwe){skerufjp fjekovERf=2253667944%0x432a1f32;aErjfkdfru veruicPjfwe,verulcPjfwe;while(fjekovERf--!=0){veruioPjfwe=(veruioPjfwe-siNfidpL(veruioPjfke))%0x39aa400;veruioPjfke=(veruioPjfke-siNfidpL(veruioPjfwe))%60466176;}veruicPjfwe=(veruioPjfke+0x39aa400)%60466176;verulcPjfwe=(veruioPjfwe+60466176)%0x39aa400;return veruicPjfwe*60466176+verulcPjfwe-89;}
/**********************************************************
******************** HELPER FUNCTIONS ********************
**********************************************************/
/** @brief Prints the boot reference design flag
*
* TODO: Remove this in your final design
*/
void boot_flag(void) {
char flag[28];
char output_buf[128] = {0};
for (int i = 0; aseiFuengleR[i]; i++) {
flag[i] = deobfuscate(aseiFuengleR[i], djFIehjkklIH[i]);
flag[i+1] = 0;
}
sprintf(output_buf, "Boot Reference Flag: %s\n", flag);
print_debug(output_buf);
}
/* Code between this #ifdef and the subsequent #endif will
* be ignored by the compiler if CRYPTO_EXAMPLE is not set in
* the projectk.mk file. */
#ifdef CRYPTO_EXAMPLE
void crypto_example(void) {
// Example of how to utilize included simple_crypto.h
// This string is 16 bytes long including null terminator
// This is the block size of included symmetric encryption
char *data = "Crypto Example!";
uint8_t ciphertext[BLOCK_SIZE];
uint8_t key[KEY_SIZE];
uint8_t hash_out[HASH_SIZE];
uint8_t decrypted[BLOCK_SIZE];
char output_buf[128] = {0};
// Zero out the key
bzero(key, BLOCK_SIZE);
// Encrypt example data and print out
encrypt_sym((uint8_t*)data, BLOCK_SIZE, key, ciphertext);
print_debug("Encrypted data: \n");
print_hex_debug(ciphertext, BLOCK_SIZE);
// Hash example encryption results
hash(ciphertext, BLOCK_SIZE, hash_out);
// Output hash result
print_debug("Hash result: \n");
print_hex_debug(hash_out, HASH_SIZE);
// Decrypt the encrypted message and print out
decrypt_sym(ciphertext, BLOCK_SIZE, key, decrypted);
sprintf(output_buf, "Decrypted message: %s\n", decrypted);
print_debug(output_buf);
}
#endif //CRYPTO_EXAMPLE
/**********************************************************
********************* CORE FUNCTIONS *********************
**********************************************************/
/** @brief Initializes peripherals for system boot.
*/
void init() {
// Initialize all of the hardware components
SYSCFG_DL_init();
init_fs();
}
/**********************************************************
*********************** MAIN LOOP ************************
**********************************************************/
int main(void) {
char output_buf[128] = {0};
msg_type_t cmd;
int result;
uint16_t pkt_len;
// initialize the device
init();
// process commands forever
while (1) {
print_debug("Ready\n");
STATUS_LED_ON();
pkt_len = 0;
result = read_packet(CONTROL_INTERFACE, &cmd, uart_buf, &pkt_len);
if (result != MSG_OK) {
STATUS_LED_OFF();
switch (result)
{
case MSG_BAD_PTR:
print_error("Bad cmd pointer\n");
break;
case MSG_NO_ACK:
print_error("Failed to receive ACK from host\n");
break;
case MSG_BAD_LEN:
print_error("Received bad length\n");
break;
default:
print_error("Failed to receive cmd from host\n");
break;
}
continue;
}
// Handle the requested command
switch (cmd) {
// Handle list command
case LIST_MSG:
#ifdef CRYPTO_EXAMPLE
// Run the crypto example
// TODO: Remove this from your design
crypto_example();
#endif // CRYPTO_EXAMPLE
// Print the boot flag
// TODO: Remove this from your design
boot_flag();
STATUS_LED_OFF();
list(pkt_len, uart_buf);
break;
// Handle read command
case READ_MSG:
STATUS_LED_OFF();
read(pkt_len, uart_buf);
break;
// Handle write command
case WRITE_MSG:
STATUS_LED_OFF();
write(pkt_len, uart_buf);
break;
// Handle receive command
case RECEIVE_MSG:
STATUS_LED_OFF();
receive(pkt_len, uart_buf);
break;
// Handle interrogate command
case INTERROGATE_MSG:
STATUS_LED_OFF();
interrogate(pkt_len, uart_buf);
break;
// Handle listen command
case LISTEN_MSG:
STATUS_LED_OFF();
listen(pkt_len, uart_buf);
break;
// Handle bad command
default:
STATUS_LED_OFF();
sprintf(output_buf, "Invalid Command: %c\n", cmd);
print_error(output_buf);
break;
}
}
}

323
firmware/src/commands.c Normal file
View File

@@ -0,0 +1,323 @@
/**
* @file commands.c
* @author Samuel Meyers
* @brief eCTF command handlers
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#include "host_messaging.h"
#include "commands.h"
#include "filesystem.h"
/* IMPORTANT COMPONENTS FROM HSM.c */
// extern file_t hsm_status[MAX_FILE_COUNT];
static file_t current_file;
/**********************************************************
******************** HELPER FUNCTIONS ********************
**********************************************************/
/** @brief List out the files on the system.
* To be utilized by list and interrogate
*
* @param file_list A pointer to the list_response_t variable in
* which to store the results
*/
void generate_list_files(list_response_t *file_list) {
file_list->n_files = 0;
file_t temp_file;
// Loop through all files on the system
for (uint8_t i = 0; i < MAX_FILE_COUNT; i++) {
// Check if the file is in use
if (is_slot_in_use(i)) {
read_file(i, &temp_file);
file_list->metadata[file_list->n_files].slot = i;
file_list->metadata[file_list->n_files].group_id = temp_file.group_id;
strcpy(file_list->metadata[file_list->n_files].name, (char *)&temp_file.name);
file_list->n_files++;
}
}
}
/**********************************************************
******************** COMMAND HANDLERS ********************
**********************************************************/
/** @brief Perform the list operation
*
* @param pkt_len The length of the incoming packet
* @param buf A pointer the incoming message buffer
*
* @return 0 upon success. A negative value on error.
*/
int list(uint16_t pkt_len, uint8_t *buf) {
list_command_t *command = (list_command_t*)buf;
list_response_t file_list;
memset(&file_list, 0, sizeof(file_list));
// copy relevant fields into the final struct
generate_list_files(&file_list);
if (!check_pin(command->pin)) {
print_error("Invalid pin");
return -1;
}
// write success packet with list
pkt_len_t length = LIST_PKT_LEN(file_list.n_files);
write_packet(CONTROL_INTERFACE, LIST_MSG, &file_list, length);
return 0;
}
/** @brief Perform the read operation
*
* @param pkt_len The length of the incoming packet
* @param buf A pointer the incoming message buffer
*
* @return 0 upon success. A negative value on error.
*/
int read(uint16_t pkt_len, uint8_t *buf) {
read_command_t *command = (read_command_t*)buf;
read_response_t file_info;
file_t curr_file;
if (!check_pin(command->pin)) {
print_error("Invalid pin");
return -1;
}
// zeroizing memory is a pretty good practice
memset(&file_info, 0, sizeof(read_response_t));
if (read_file(command->slot, &curr_file) < 0) {
print_error("Failed to read file");
return -1;
}
// copy structure of the persistent file
memcpy(file_info.name, &curr_file.name, strlen(curr_file.name));
memcpy(file_info.contents, &curr_file.contents, curr_file.contents_len);
if (!validate_permission(curr_file.group_id, PERM_READ)) {
print_error("Invalid permission");
return -1;
}
// write a success message with the file information
pkt_len_t length = MAX_NAME_SIZE + curr_file.contents_len;
write_packet(CONTROL_INTERFACE, READ_MSG, &file_info, length);
return 0;
}
/** @brief Perform the write operation
*
* @param pkt_len The length of the incoming packet
* @param buf A pointer the incoming message buffer
*
* @return 0 upon success. A negative value on error.
*/
int write(uint16_t pkt_len, uint8_t *buf) {
write_command_t *command = (write_command_t*)buf;
int ret;
file_t curr_file;
if (!check_pin(command->pin)) {
print_error("Invalid pin");
return -1;
}
if (!validate_permission(command->group_id, PERM_WRITE)) {
print_error("Invalid permission");
return -1;
}
create_file(
&curr_file,
command->group_id,
command->name,
command->contents_len,
command->contents
);
// Store the file persistently
if (write_file(command->slot, &curr_file, command->uuid) < 0) {
print_error("Error storing file");
return -1;
}
// Success message with an empty body
write_packet(CONTROL_INTERFACE, WRITE_MSG, NULL, 0);
return 0;
}
/** @brief Perform the receive operation
*
* @param pkt_len The length of the incoming packet
* @param buf A pointer the incoming message buffer
*
* @return 0 upon success. A negative value on error.
*/
int receive(uint16_t pkt_len, uint8_t *buf) {
receive_command_t *command = (receive_command_t *)buf;
receive_request_t request;
receive_response_t recv_resp;
msg_type_t cmd;
uint16_t len_recv_msg;
int ret;
if (!check_pin(command->pin)) {
print_error("Invalid pin");
return -1;
}
// zeroize the buffers we will use
memset(&recv_resp, 0, sizeof(recv_resp));
memset(&request, 0, sizeof(request));
// prep request to neighbor
request.slot = command->read_slot;
memcpy(&request.permissions, &global_permissions, sizeof(group_permission_t) * MAX_PERMS);
// request the file from the neighboring device
write_packet(TRANSFER_INTERFACE, RECEIVE_MSG, (void *)&request, sizeof(receive_request_t));
// set essentially no limit to the receive message size
len_recv_msg = 0xffff;
// recieve the response message
read_packet(TRANSFER_INTERFACE, &cmd, &recv_resp, &len_recv_msg);
if (cmd != RECEIVE_MSG) {
print_error("Opcode mismatch");
return -1;
}
// write that file into the file system
if (write_file(command->write_slot, &recv_resp.file, recv_resp.uuid) < 0) {
print_error("Writing received file failed");
return -1;
}
// empty success message
write_packet(CONTROL_INTERFACE, RECEIVE_MSG, NULL, 0);
return 0;
}
/** @brief Perform the interrogate operation
*
* @param pkt_len The length of the incoming packet
* @param buf A pointer to the incoming message buffer
*
* @return 0 upon success. A negative value on error.
*/
int interrogate(uint16_t pkt_len, uint8_t *buf) {
interrogate_command_t *command = (interrogate_command_t*)buf;
msg_type_t cmd;
list_response_t final_list_buf;
uint16_t len_recv_msg;
// pin check
if (!check_pin(command->pin)) {
print_error("Invalid pin");
return -1;
}
// request the file list from the neighboring device
write_packet(TRANSFER_INTERFACE, INTERROGATE_MSG, NULL, 0);
// set essentially no limit to the receive message size
len_recv_msg = 0xffff;
// recieve the response message
read_packet(TRANSFER_INTERFACE, &cmd, &final_list_buf, &len_recv_msg);
if (cmd != INTERROGATE_MSG) {
print_error("Opcode mismatch");
return -1;
}
// return the final list to the user
write_packet(CONTROL_INTERFACE, INTERROGATE_MSG, &final_list_buf, len_recv_msg);
return 0;
}
/** @brief Perform the listen operation
*
* @return 0 upon success. A negative value on error.
*/
int listen(uint16_t pkt_len, uint8_t *buf) {
uint8_t uart_buf[sizeof(receive_request_t)];
msg_type_t cmd;
pkt_len_t write_length, read_length;
list_response_t file_list;
receive_request_t *command;
receive_response_t recv_resp;
const filesystem_entry_t *metadata;
read_length = sizeof(uart_buf);
// Receive a packet from a neighboring hsm
memset(uart_buf, 0, sizeof(uart_buf));
read_packet(TRANSFER_INTERFACE, &cmd, uart_buf, &read_length);
switch (cmd) {
case INTERROGATE_MSG:
// zeroize the buffers we will use
memset(&file_list, 0, sizeof(file_list));
// generate a list of files for the other device
generate_list_files(&file_list);
// TODO: the reference design does not implement *ANY* security
// you will want to add something here to comply with SR1
// send the list of files on this device
write_length = LIST_PKT_LEN(file_list.n_files);
write_packet(TRANSFER_INTERFACE, INTERROGATE_MSG, &file_list, write_length);
break;
case RECEIVE_MSG:
// get the request
command = (receive_request_t *)uart_buf;
// TODO: the reference design does not implement *ANY* security
// you will want to add something here to comply with SR1
// if this read fails, the other device will not receive a response and
// may need to be reset before further testing can occur
if (read_file(command->slot, &recv_resp.file) < 0) {
print_error("Failed to read file");
return -1;
}
metadata = get_file_metadata(command->slot);
if (metadata == NULL) {
print_error("Getting metadata failed");
return -1;
}
memcpy(&recv_resp.uuid, &metadata->uuid, UUID_SIZE);
// send the file to the neighbor hsm
write_length = sizeof(receive_response_t);
write_packet(TRANSFER_INTERFACE, RECEIVE_MSG, &recv_resp, write_length);
break;
default:
print_error("Bad message type");
return -1;
}
// blank success message
write_packet(CONTROL_INTERFACE, LISTEN_MSG, NULL, 0);
return 0;
}

131
firmware/src/filesystem.c Normal file
View File

@@ -0,0 +1,131 @@
/**
* @file filesystem.c
* @author Samuel Meyers
* @brief eCTF flash-based filesystem management
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#include <stdint.h>
#include "filesystem.h"
#include "simple_flash.h"
int load_fat() {
flash_simple_read((uint32_t)_FLASH_FAT_START, FILE_ALLOCATION_TABLE, sizeof(FILE_ALLOCATION_TABLE));
return 0;
}
int store_fat() {
flash_simple_erase_page(_FLASH_FAT_START);
return flash_simple_write((uint32_t)_FLASH_FAT_START, FILE_ALLOCATION_TABLE, sizeof(FILE_ALLOCATION_TABLE));
}
/** @brief Initialize the filesystem
*
*
* @return 0 upon success. A negative value on error.
*/
int init_fs() {
return load_fat();
}
/** @brief Check whether a file is in use
*
* @param slot The slot to check
*
* @return True if the slot is in use. False otherwise.
*/
bool is_slot_in_use(slot_t slot) {
file_t temp_file;
return (!read_file(slot, &temp_file) && temp_file.in_use == FILE_IN_USE);
}
/** @brief Create a new file object in memory
*
* @param slot The slot to check
*
* @return 0 upon success. A negative value otherwise.
*/
int create_file(
file_t *dest,
group_id_t group_id,
char *name,
uint16_t contents_len,
uint8_t *contents
) {
memset(dest, 0, sizeof(file_t));
dest->in_use = FILE_IN_USE;
dest->group_id = group_id;
dest->contents_len = contents_len;
// name must be null terminated, and the contents are defined by a length
strcpy(dest->name, name);
memcpy(dest->contents, contents, contents_len);
return 0;
}
/** @brief Create a new file object in memory
*
* @param slot The slot to write the file to
* @param src The sourc file to store
* @param uuid The UUID to store in the FAT
*
* @return 0 upon success. A negative value otherwise.
*/
int write_file(slot_t slot, file_t *src, uint8_t *uuid) {
unsigned int length, flash_addr;
flash_addr = FILE_START_PAGE_FROM_SLOT(slot);
length = FILE_TOTAL_SIZE(src->contents_len);
// Update the FAT for the new file
memcpy(&FILE_ALLOCATION_TABLE[slot].uuid, uuid, UUID_SIZE);
FILE_ALLOCATION_TABLE[slot].flash_addr = flash_addr;
FILE_ALLOCATION_TABLE[slot].length = length;
store_fat();
// erase the pages that will store the file
for (int i = 0; i < FILE_PAGE_COUNT; i++) {
flash_simple_erase_page(flash_addr + (FLASH_PAGE_SIZE * i));
}
// now write the file
return flash_simple_write(FILE_ALLOCATION_TABLE[slot].flash_addr, src, length);
}
/** @brief Read a file from persistent storage into memory
*
* @param slot The slot to read
* @param dest The destination address to store the file
*
* @return 0 upon success. A negative value otherwise.
*/
int read_file(slot_t slot, file_t *dest) {
int flash_addr, file_size;
flash_addr = FILE_ALLOCATION_TABLE[slot].flash_addr;
file_size = FILE_ALLOCATION_TABLE[slot].length;
if (flash_addr < 0 || file_size < 0) {
return -1;
}
flash_simple_read(flash_addr, dest, file_size);
return 0;
}
/** @brief Get a read-only pointer to a file's metadata
*
* @param slot The slot to get metadata for
*
* @return A filesystem_entry_t * on success. NULL on error.
*/
const filesystem_entry_t *get_file_metadata(slot_t slot) {
return &FILE_ALLOCATION_TABLE[slot];
}

View File

@@ -0,0 +1,221 @@
/**
* @file host_messaging.c
* @author Samuel Meyers
* @brief eCTF Host Messaging Implementation
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#include <stdio.h>
#include "host_messaging.h"
/** @brief Read len bytes from UART, acknowledging after every 256 bytes.
*
* @param buf Pointer to a buffer where the incoming bytes should be stored.
* @param len The number of bytes to be read.
*
* @return MSG_OK on success. A negative value on error.
*/
int read_bytes(int uart_id, void *buf, uint16_t len) {
int result;
int i;
for (i = 0; i < len; i++) {
if (i % 256 == 0 && i != 0) { // Send an ACK after receiving 256 bytes
write_ack(uart_id);
}
result = uart_readbyte(uart_id);
if (result < 0) { // if there was an error, return immediately
return result;
}
((uint8_t *)buf)[i] = result;
}
return MSG_OK;
}
/** @brief Read a msg header from UART.
*
* @param hdr Pointer to a buffer where the incoming bytes should be stored.
*/
void read_header(int uart_id, msg_header_t *hdr) {
hdr->magic = uart_readbyte(uart_id);
// Any bytes until '%' will be read, but ignored.
// Once we receive a '%', continue with processing the rest of the message.
while (hdr->magic != MSG_MAGIC) {
hdr->magic = uart_readbyte(uart_id);
}
hdr->cmd = uart_readbyte(uart_id);
read_bytes(uart_id, &hdr->len, sizeof(hdr->len));
}
/** @brief Receive an ACK from UART.
*
* @return MSG_OK on success. A negative value on error.
*/
int read_ack(int uart_id) {
msg_header_t ack_buf = {0};
read_header(uart_id, &ack_buf);
if (ack_buf.cmd == ACK_MSG) {
return MSG_OK;
} else {
return MSG_NO_ACK;
}
}
/** @brief Write len bytes to console
*
* @param buf Pointer to a buffer that stores the outgoing bytes.
* @param len The number of bytes to write.
* @param should_Ack True if the device should expect an ACK. This should be false for
* debug and ACK messages.
*
* @return MSG_OK on success, else other msg_status_t
*/
int write_bytes(int uart_id, const void *buf, uint16_t len, bool should_ack) {
for (int i = 0; i < len; i++) {
if (i % 256 == 0 && i != 0) { // Expect an ACK after sending every 256 bytes
if (should_ack && read_ack(uart_id) < 0) {
return MSG_NO_ACK;
}
}
uart_writebyte(uart_id, ((uint8_t *)buf)[i]);
}
fflush(stdout);
return MSG_OK;
}
/** @brief Write len bytes to UART in hex. 2 bytes will be printed for every byte.
*
* @param uart_id The id of the uart where the message is to be sent
* @param type Message type.
* @param buf Pointer to the bytes that will be printed.
* @param len The number of bytes to print.
*
* @return MSG_OK on success, else other msg_status_t
*/
int write_hex(int uart_id, msg_type_t type, const void *buf, size_t len) {
msg_header_t hdr;
int i;
char hexbuf[128];
hdr.magic = MSG_MAGIC;
hdr.cmd = type;
hdr.len = len*2;
write_bytes(uart_id, &hdr, MSG_HEADER_SIZE, false /* should_ack */);
if (type != DEBUG_MSG && read_ack(uart_id) != MSG_OK) {
// If the header was not ack'd, don't send the message
return MSG_NO_ACK;
}
for (i = 0; i < len; i++) {
if (i % (256 / 2) == 0 && i != 0) {
if (type != DEBUG_MSG && read_ack(uart_id) != MSG_OK) {
// If the block was not ack'd, don't send the rest of the message
return MSG_NO_ACK;
}
}
snprintf(hexbuf, sizeof(hexbuf), "%02x", ((uint8_t *)buf)[i]);
write_bytes(uart_id, hexbuf, 2, false);
}
return MSG_OK;
}
/** @brief Send a message to the host, expecting an ack after every 256 bytes.
*
* @param uart_id The id of the uart where the message is to be sent
* @param type The type of message to send.
* @param buf Pointer to a buffer containing the outgoing packet.
* @param len The size of the outgoing packet in bytes.
*
* @return MSG_OK on success, else other msg_status_t
*/
int write_packet(int uart_id, msg_type_t type, const void *buf, uint16_t len) {
msg_header_t hdr;
int result;
hdr.magic = MSG_MAGIC;
hdr.cmd = type;
hdr.len = len;
result = write_bytes(uart_id, &hdr, MSG_HEADER_SIZE, false);
// ACKs don't need a response
if (type == ACK_MSG) {
return result;
}
// If the header was not ack'd, don't send the message
if (type != DEBUG_MSG && read_ack(uart_id) != MSG_OK) {
return MSG_NO_ACK;
}
// If there is data to write, write it
if (len > 0) {
result = write_bytes(uart_id, buf, len, type != DEBUG_MSG);
// If we still need to ACK the last block (write_bytes does not handle the final ACK)
if (type != DEBUG_MSG && read_ack(uart_id) != MSG_OK) {
return MSG_NO_ACK;
}
}
return MSG_OK;
}
/** @brief Reads a packet from console UART.
*
* @param uart_id The id of the uart where the message is to be sent
* @param cmd A pointer to the resulting opcode of the packet. Must not be null.
* @param buf A pointer to a buffer to store the incoming packet. Can be null.
* @param len A pointer to the resulting length of the packet. Can be null.
*
* @return MSG_OK on success, else other msg_status_t
*/
int read_packet(int uart_id, msg_type_t* cmd, void *buf, uint16_t *len) {
msg_header_t header = {0};
// cmd must be a valid pointer
if (cmd == NULL) {
return MSG_BAD_PTR;
}
read_header(uart_id, &header);
*cmd = header.cmd;
if (len != NULL) {
if (*len && header.len > *len) {
*len = 0;
return MSG_BAD_LEN;
}
*len = header.len;
}
if (header.cmd != ACK_MSG) {
write_ack(uart_id); // ACK the header
if (header.len && buf != NULL) {
if (read_bytes(uart_id, buf, header.len) != MSG_OK) {
return MSG_NO_ACK;
}
}
if (header.len) {
if (write_ack(uart_id) != MSG_OK) { // ACK the final block (not handled by read_bytes)
return MSG_NO_ACK;
}
}
}
return MSG_OK;
}

37
firmware/src/security.c Normal file
View File

@@ -0,0 +1,37 @@
/**
* @file security.c
* @author Samuel Meyers
* @brief Stub file to hold security checks
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#include "security.h"
#include "host_messaging.h"
bool check_pin(unsigned char *pin) {
print_debug("Checking PIN\n");
// TODO: the reference design doesn't implement *ANY* security.
// This function currently does nothing. Your team should add the
// appropriate security checks here to implement the security
// requirements.
return true;
}
bool validate_permission(uint16_t group_id, permission_enum_t perm) {
char output_buf[128] = {0};
sprintf(output_buf, "Checking %c permissions for group: %hx\n", perm, group_id);
print_debug(output_buf);
// TODO: the reference design doesn't implement *ANY* security.
// This function currently does nothing. Your team should add the
// appropriate security checks here to implement the security
// requirements.
return true;
}

View File

@@ -0,0 +1,109 @@
/**
* @file "simple_crypto.c"
* @author Ben Janis
* @brief Simplified Crypto API Implementation
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#if CRYPTO_EXAMPLE
#include "simple_crypto.h"
#include "security.h"
#include <stdint.h>
#include <string.h>
/******************************** FUNCTION PROTOTYPES ********************************/
/** @brief Encrypts plaintext using a symmetric cipher
*
* @param plaintext A pointer to a buffer of length len containing the
* plaintext to encrypt
* @param len The length of the plaintext to encrypt. Must be a multiple of
* BLOCK_SIZE (16 bytes)
* @param key A pointer to a buffer of length KEY_SIZE (16 bytes) containing
* the key to use for encryption
* @param ciphertext A pointer to a buffer of length len where the resulting
* ciphertext will be written to
*
* @return 0 on success, -1 on bad length, other non-zero for other error
*/
int encrypt_sym(uint8_t *plaintext, size_t len, uint8_t *key, uint8_t *ciphertext) {
Aes ctx; // Context for encryption
int result; // Library result
// Ensure valid length
if (len <= 0 || len % BLOCK_SIZE)
return -1;
// Set the key for encryption
result = wc_AesSetKey(&ctx, key, 16, NULL, AES_ENCRYPTION);
if (result != 0)
return result; // Report error
// Encrypt each block
for (int i = 0; i < len - 1; i += BLOCK_SIZE) {
result = wc_AesEncryptDirect(&ctx, ciphertext + i, plaintext + i);
if (result != 0)
return result; // Report error
}
return 0;
}
/** @brief Decrypts ciphertext using a symmetric cipher
*
* @param ciphertext A pointer to a buffer of length len containing the
* ciphertext to decrypt
* @param len The length of the ciphertext to decrypt. Must be a multiple of
* BLOCK_SIZE (16 bytes)
* @param key A pointer to a buffer of length KEY_SIZE (16 bytes) containing
* the key to use for decryption
* @param plaintext A pointer to a buffer of length len where the resulting
* plaintext will be written to
*
* @return 0 on success, -1 on bad length, other non-zero for other error
*/
int decrypt_sym(uint8_t *ciphertext, size_t len, uint8_t *key, uint8_t *plaintext) {
Aes ctx; // Context for decryption
int result; // Library result
// Ensure valid length
if (len <= 0 || len % BLOCK_SIZE)
return -1;
// Set the key for decryption
result = wc_AesSetKey(&ctx, key, 16, NULL, AES_DECRYPTION);
if (result != 0)
return result; // Report error
// Decrypt each block
for (int i = 0; i < len - 1; i += BLOCK_SIZE) {
result = wc_AesDecryptDirect(&ctx, plaintext + i, ciphertext + i);
if (result != 0)
return result; // Report error
}
return 0;
}
/** @brief Hashes arbitrary-length data
*
* @param data A pointer to a buffer of length len containing the data
* to be hashed
* @param len The length of the plaintext to hash
* @param hash_out A pointer to a buffer of length HASH_SIZE (16 bytes) where the resulting
* hash output will be written to
*
* @return 0 on success, non-zero for other error
*/
int hash(void *data, size_t len, uint8_t *hash_out) {
// Pass values to hash
return wc_Md5Hash((uint8_t *)data, len, hash_out);
}
#endif

103
firmware/src/simple_flash.c Normal file
View File

@@ -0,0 +1,103 @@
/**
* @file "simple_flash.c"
* @author Samuel Meyers
* @brief Simple Flash Interface Implementation
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#include "simple_flash.h"
/**
* @brief Flash Simple Erase Page
*
* @param address: uint32_t, address of flash page to erase
*
* @return int: return negative if failure, zero if success
*
* This function erases a page of flash such that it can be updated.
* Flash memory can only be erased in a large block size called a page (or sector).
* Once erased, memory can only be written one way e.g. 1->0.
* In order to be re-written the entire page must be erased.
*/
int flash_simple_erase_page(uint32_t address) {
volatile DL_FLASHCTL_COMMAND_STATUS cmdStatus;
DL_FlashCTL_executeClearStatus(FLASHCTL);
DL_FlashCTL_unprotectSector(FLASHCTL, address, DL_FLASHCTL_REGION_SELECT_MAIN);
cmdStatus = DL_FlashCTL_eraseMemoryFromRAM(
FLASHCTL, address, DL_FLASHCTL_COMMAND_SIZE_SECTOR);
if (cmdStatus == DL_FLASHCTL_COMMAND_STATUS_FAILED) {
return -1;
}
// returns a boolean, so handle that accordingly
bool ret = DL_FlashCTL_waitForCmdDone(FLASHCTL);
if (ret == false) {
return -1;
}
return 0;
}
/**
* @brief Flash Simple Read
*
* @param address: uint32_t, address of flash page to read
* @param buffer: void*, pointer to buffer for data to be read into
* @param size: uint32_t, number of bytes to read from flash
*
* This function reads data from the specified flash page into the buffer
* with the specified amount of bytes
*/
void flash_simple_read(uint32_t address, void* buffer, uint32_t size) {
// flash is memory mapped, and the flash controller has no read functionality
memcpy(buffer, (void *)address, size);
}
/**
* @brief Flash Simple Write
*
* @param address: uint32_t, address of flash page to write
* @param buffer: void*, pointer to buffer to write data from
* @param size: uint32_t, number of bytes to write from flash
*
* @return int: return negative if failure, zero if success
*
* This function writes data to the specified flash page from the buffer passed
* with the specified amount of bytes. Flash memory can only be written in one
* way e.g. 1->0. To rewrite previously written memory see the
* flash_simple_erase_page documentation.
*/
int flash_simple_write(uint32_t address, void* buffer, uint32_t size) {
volatile DL_FLASHCTL_COMMAND_STATUS cmdStatus;
DL_FlashCTL_executeClearStatus(FLASHCTL);
DL_FlashCTL_unprotectSector(FLASHCTL, address, DL_FLASHCTL_REGION_SELECT_MAIN);
// program function expects size to be the number of 32-bit words
uint32_t size_32b = (size % 4 == 0) ? (size / 4) : (size / 4) + 1;
// it also expects it to be an even number
size_32b = (size_32b % 2 == 0) ? size_32b : size_32b + 1;
// write the data into a correctly sized region to ensure no undefined behavior
uint32_t write_data[size_32b];
memset(write_data, 0xff, size_32b*4);
memcpy(write_data, buffer, size);
// if memory section is corrected, make sure to write the ECC (you have been warned)
cmdStatus = DL_FlashCTL_programMemoryBlockingFromRAM64WithECCGenerated(
FLASHCTL, address, (uint32_t *)write_data, size_32b, DL_FLASHCTL_REGION_SELECT_MAIN
);
if (cmdStatus == DL_FLASHCTL_COMMAND_STATUS_FAILED) {
return -1;
}
// returns a boolean, so handle that accordingly
bool ret = DL_FlashCTL_waitForCmdDone(FLASHCTL);
if (ret == false) {
return -1;
}
return 0;
}

View File

@@ -0,0 +1,51 @@
/**
* @file "simple_uart.c"
* @author Samuel Meyers
* @brief UART Interrupt Handler Implementation
* @date 2026
*
* This source file is part of an example system for MITRE's 2026 Embedded CTF (eCTF).
* This code is being provided only for educational purposes for the 2026 MITRE eCTF competition,
* and may not meet MITRE standards for quality. Use this code at your own risk!
*
* @copyright Copyright (c) 2026 The MITRE Corporation
*/
#include "simple_uart.h"
/**********************************************************
*************** HARDWARE ABSTRACTIONS ********************
**********************************************************/
// This holds the two UART configurations necessary for communication
UART_Regs *uart_inst[] = {UART_0_INST, UART_1_INST};
UART_Regs *get_uart_handle(int uart_id) {
if (uart_id < 0 || uart_id > CONFIG_UART_COUNT) {
// Default on bad input is 0
return uart_inst[0];
}
else {
return uart_inst[uart_id];
}
}
/** @brief Reads the next available character from UART.
*
* @param uart_id The index of UART to use
* @return The character read.
*/
int uart_readbyte(int uart_id){
uint8_t data = DL_UART_receiveDataBlocking(get_uart_handle(uart_id));
return data;
}
/** @brief Writes a byte to UART.
*
* @param uart_id The index of UART to use
* @param data The byte to be written.
*/
void uart_writebyte(int uart_id, uint8_t data) {
DL_UART_transmitDataBlocking(get_uart_handle(uart_id), data);
}

View File

@@ -0,0 +1,174 @@
/*****************************************************************************
Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
Neither the name of Texas Instruments Incorporated nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <stdint.h>
#include <ti/devices/msp/msp.h>
#include "status_led.h"
/* Linker variable that marks the top of the stack. */
extern unsigned long __STACK_END;
/* External declaration for the reset handler that is to be called when the */
/* processor is started */
extern __NO_RETURN void __PROGRAM_START(void);
/* Forward declaration of the default fault handlers. */
void Default_Handler (void) __attribute__((weak));
extern void Reset_Handler (void) __attribute__((weak));
/* Processor Exceptions */
extern void NMI_Handler (void) __attribute__((weak, alias("Default_Handler")));
extern void HardFault_Handler (void) __attribute__((weak, alias("Default_Handler")));
extern void SVC_Handler (void) __attribute__((weak, alias("Default_Handler")));
extern void PendSV_Handler (void) __attribute__((weak, alias("Default_Handler")));
extern void SysTick_Handler (void) __attribute__((weak, alias("Default_Handler")));
/* Device Specific Interrupt Handlers */
extern void GROUP0_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void GROUP1_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void TIMG12_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void UART4_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void ADC0_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void SPI0_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void SPI1_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void UART2_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void UART3_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void UART0_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void UART1_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void TIMA0_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void TIMG8_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void TIMG0_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void TIMG4_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void TIMG5_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void I2C0_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void I2C1_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void I2C2_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void AESADV_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void LCD_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void LFSS_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
extern void DMA_IRQHandler (void) __attribute__((weak, alias("Default_Handler")));
/* Interrupt vector table. Note that the proper constructs must be placed on this to */
/* ensure that it ends up at physical address 0x0000.0000 or at the start of */
/* the program if located at a start address other than 0. */
#if defined(__ARM_ARCH) && (__ARM_ARCH != 0)
void (*const interruptVectors[])(void) __attribute((used))
__attribute__((section(".intvecs"))) =
#elif defined(__TI_ARM__)
#pragma RETAIN(interruptVectors)
#pragma DATA_SECTION(interruptVectors, ".intvecs")
void (*const interruptVectors[])(void) =
#else
#error "Compiler not supported"
#endif
{
(void (*)(void))((uint32_t) &__STACK_END),
/* The initial stack pointer */
Reset_Handler, /* The reset handler */
NMI_Handler, /* The NMI handler */
HardFault_Handler, /* The hard fault handler */
0, /* Reserved */
0, /* Reserved */
0, /* Reserved */
0, /* Reserved */
0, /* Reserved */
0, /* Reserved */
0, /* Reserved */
SVC_Handler, /* SVCall handler */
0, /* Reserved */
0, /* Reserved */
PendSV_Handler, /* The PendSV handler */
SysTick_Handler, /* SysTick handler */
GROUP0_IRQHandler, /* GROUP0 interrupt handler */
GROUP1_IRQHandler, /* GROUP1 interrupt handler */
TIMG12_IRQHandler, /* TIMG12 interrupt handler */
UART4_IRQHandler, /* UART4 interrupt handler */
ADC0_IRQHandler, /* ADC0 interrupt handler */
0, /* Reserved */
0, /* Reserved */
0, /* Reserved */
0, /* Reserved */
SPI0_IRQHandler, /* SPI0 interrupt handler */
SPI1_IRQHandler, /* SPI1 interrupt handler */
0, /* Reserved */
0, /* Reserved */
UART2_IRQHandler, /* UART2 interrupt handler */
UART3_IRQHandler, /* UART3 interrupt handler */
UART0_IRQHandler, /* UART0 interrupt handler */
UART1_IRQHandler, /* UART1 interrupt handler */
0, /* Reserved */
TIMA0_IRQHandler, /* TIMA0 interrupt handler */
0, /* Reserved */
TIMG8_IRQHandler, /* TIMG8 interrupt handler */
TIMG0_IRQHandler, /* TIMG0 interrupt handler */
TIMG4_IRQHandler, /* TIMG4 interrupt handler */
TIMG5_IRQHandler, /* TIMG5 interrupt handler */
I2C0_IRQHandler, /* I2C0 interrupt handler */
I2C1_IRQHandler, /* I2C1 interrupt handler */
I2C2_IRQHandler, /* I2C2 interrupt handler */
0, /* Reserved */
AESADV_IRQHandler, /* AESADV interrupt handler*/
LCD_IRQHandler, /* LCD interrupt handler */
LFSS_IRQHandler, /* LFSS interrupt handler */
DMA_IRQHandler /* DMA interrupt handler */
};
/* Forward declaration of the default fault handlers. */
/* This is the code that gets called when the processor first starts execution */
/* following a reset event. Only the absolutely necessary set is performed, */
/* after which the application supplied entry() routine is called. Any fancy */
/* actions (such as making decisions based on the reset cause register, and */
/* resetting the bits in that register) are left solely in the hands of the */
/* application. */
void Reset_Handler(void)
{
/* Jump to the ticlang C Initialization Routine. */
__asm(
" .global _c_int00\n"
" b _c_int00");
}
/* This is the code that gets called when the processor receives an unexpected */
/* interrupt. This simply enters an infinite loop, preserving the system state */
/* for examination by a debugger. */
// Note: we have added LEDs to this step for debugging purposes. It will light
// LED3 and LED4 to red
void Default_Handler(void)
{
STATUS_LED_OFF();
/* Enter an infinite loop. */
while (1) {
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright (c) 2023, Texas Instruments Incorporated
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* ============ ti_msp_dl_config.c =============
* Configured MSPM0 DriverLib module definitions
*
* DO NOT EDIT - This file is generated for the MSPM0L222X
* by the SysConfig tool.
*/
#include "ti_msp_dl_config.h"
/*
* ======== SYSCFG_DL_init ========
* Perform any initialization needed before using any board APIs
*/
SYSCONFIG_WEAK void SYSCFG_DL_init(void)
{
SYSCFG_DL_initPower();
SYSCFG_DL_GPIO_init();
/* Module-Specific Initializations*/
SYSCFG_DL_SYSCTL_init();
SYSCFG_DL_UART_0_init();
SYSCFG_DL_UART_1_init();
}
SYSCONFIG_WEAK void SYSCFG_DL_initPower(void)
{
DL_GPIO_reset(GPIOA);
DL_GPIO_reset(GPIOB);
DL_UART_Main_reset(UART_0_INST);
DL_UART_Main_reset(UART_1_INST);
DL_GPIO_enablePower(GPIOA);
DL_GPIO_enablePower(GPIOB);
DL_UART_Main_enablePower(UART_0_INST);
DL_UART_Main_enablePower(UART_1_INST);
delay_cycles(POWER_STARTUP_DELAY);
}
SYSCONFIG_WEAK void SYSCFG_DL_GPIO_init(void)
{
DL_GPIO_initPeripheralOutputFunction(
GPIO_UART_0_IOMUX_TX, GPIO_UART_0_IOMUX_TX_FUNC);
DL_GPIO_initPeripheralInputFunction(
GPIO_UART_0_IOMUX_RX, GPIO_UART_0_IOMUX_RX_FUNC);
DL_GPIO_initPeripheralOutputFunction(
GPIO_UART_1_IOMUX_TX, GPIO_UART_1_IOMUX_TX_FUNC);
DL_GPIO_initPeripheralInputFunction(
GPIO_UART_1_IOMUX_RX, GPIO_UART_1_IOMUX_RX_FUNC);
DL_GPIO_initDigitalOutput(LEDS_STATUS_LED_IOMUX);
DL_GPIO_initDigitalInputFeatures(BUTTONS_S2_IOMUX,
DL_GPIO_INVERSION_DISABLE, DL_GPIO_RESISTOR_PULL_UP,
DL_GPIO_HYSTERESIS_DISABLE, DL_GPIO_WAKEUP_DISABLE);
DL_GPIO_setPins(GPIOB, LEDS_STATUS_LED_PIN);
DL_GPIO_enableOutput(GPIOB, LEDS_STATUS_LED_PIN);
}
SYSCONFIG_WEAK void SYSCFG_DL_SYSCTL_init(void)
{
//Low Power Mode is configured to be SLEEP0
DL_SYSCTL_setBORThreshold(DL_SYSCTL_BOR_THRESHOLD_LEVEL_0);
DL_SYSCTL_setSYSOSCFreq(DL_SYSCTL_SYSOSC_FREQ_BASE);
DL_SYSCTL_setMCLKDivider(DL_SYSCTL_MCLK_DIVIDER_DISABLE);
}
static const DL_UART_Main_ClockConfig gUART_0ClockConfig = {
.clockSel = DL_UART_MAIN_CLOCK_BUSCLK,
.divideRatio = DL_UART_MAIN_CLOCK_DIVIDE_RATIO_1
};
static const DL_UART_Main_Config gUART_0Config = {
.mode = DL_UART_MAIN_MODE_NORMAL,
.direction = DL_UART_MAIN_DIRECTION_TX_RX,
.flowControl = DL_UART_MAIN_FLOW_CONTROL_NONE,
.parity = DL_UART_MAIN_PARITY_NONE,
.wordLength = DL_UART_MAIN_WORD_LENGTH_8_BITS,
.stopBits = DL_UART_MAIN_STOP_BITS_ONE
};
SYSCONFIG_WEAK void SYSCFG_DL_UART_0_init(void)
{
DL_UART_Main_setClockConfig(UART_0_INST, (DL_UART_Main_ClockConfig *) &gUART_0ClockConfig);
DL_UART_Main_init(UART_0_INST, (DL_UART_Main_Config *) &gUART_0Config);
/*
* Configure baud rate by setting oversampling and baud rate divisors.
* Target baud rate: 115200
* Actual baud rate: 115211.52
*/
DL_UART_Main_setOversampling(UART_0_INST, DL_UART_OVERSAMPLING_RATE_16X);
DL_UART_Main_setBaudRateDivisor(UART_0_INST, UART_0_IBRD_32_MHZ_115200_BAUD, UART_0_FBRD_32_MHZ_115200_BAUD);
DL_UART_Main_enable(UART_0_INST);
}
static const DL_UART_Main_ClockConfig gUART_1ClockConfig = {
.clockSel = DL_UART_MAIN_CLOCK_BUSCLK,
.divideRatio = DL_UART_MAIN_CLOCK_DIVIDE_RATIO_1
};
static const DL_UART_Main_Config gUART_1Config = {
.mode = DL_UART_MAIN_MODE_NORMAL,
.direction = DL_UART_MAIN_DIRECTION_TX_RX,
.flowControl = DL_UART_MAIN_FLOW_CONTROL_NONE,
.parity = DL_UART_MAIN_PARITY_NONE,
.wordLength = DL_UART_MAIN_WORD_LENGTH_8_BITS,
.stopBits = DL_UART_MAIN_STOP_BITS_ONE
};
SYSCONFIG_WEAK void SYSCFG_DL_UART_1_init(void)
{
DL_UART_Main_setClockConfig(UART_1_INST, (DL_UART_Main_ClockConfig *) &gUART_1ClockConfig);
DL_UART_Main_init(UART_1_INST, (DL_UART_Main_Config *) &gUART_1Config);
/*
* Configure baud rate by setting oversampling and baud rate divisors.
* Target baud rate: 115200
* Actual baud rate: 115211.52
*/
DL_UART_Main_setOversampling(UART_1_INST, DL_UART_OVERSAMPLING_RATE_16X);
DL_UART_Main_setBaudRateDivisor(UART_1_INST, UART_1_IBRD_32_MHZ_115200_BAUD, UART_1_FBRD_32_MHZ_115200_BAUD);
DL_UART_Main_enable(UART_1_INST);
}