This is a firmware project for the STM32H573VITX microcontroller, designed as a secure keycard shell for cryptocurrency operations. The firmware implements QR code scanning, smartcard communication (ISO 7816), USB HID interface, and cryptographic operations.
- MCU: STM32H573VITX (ARM Cortex-M33)
- Memory: 2MB Flash (2 banks of 1MB each), 640KB RAM
- Peripherals:
- DCMI (Camera interface)
- SMARTCARD (ISO 7816 smartcard)
- USB FS (HID device)
- SPI (LCD display)
- I2C (Camera control)
- PKA (Public Key Accelerator)
- RNG (Random Number Generator)
- HASH (SHA2/SHA3 hardware)
keycard-shell/
├── app/ # Application code
│ ├── core/ # Core business logic (keycard operations, signing)
│ ├── crypto/ # Cryptographic primitives (ECC, hashing, encoding)
│ ├── ethereum/ # Ethereum-specific operations
│ ├── iso7816/ # Smartcard protocol implementation
│ ├── qrcode/ # QR code generation and scanning
│ ├── screen/ # LCD display driver and rendering
│ ├── tasks/ # FreeRTOS task implementations
│ ├── ui/ # User interface components
│ ├── usb/ # USB HID implementation
│ ├── camera/ # Camera driver
│ ├── mem.c # Memory area definitions
│ ├── mem.h # Memory area declarations
│ ├── hal.h # Hardware Abstraction Layer
│ ├── common.h # Common macros and types
│ └── main.c # Application entry point
├── bootloader/ # Bootloader code
├── stm32/ # STM32 HAL and startup code
│ ├── Core/
│ │ ├── Inc/ # Header files
│ │ └── Src/ # Source files
│ └── Drivers/ # STM32 HAL drivers
├── freertos/ # FreeRTOS kernel
└── tools/ # Build and utility scripts
CRITICAL: This firmware does NOT use dynamic heap allocation. The following functions are FORBIDDEN:
malloc()calloc()realloc()free()
FreeRTOS is configured with configSUPPORT_DYNAMIC_ALLOCATION = 0, meaning the standard heap allocation functions are disabled.
All memory must be allocated statically at compile time. The project uses several approaches:
- Global variables for persistent data
- Stack allocation for local variables (careful with stack depth)
- Custom memory pools for temporary allocations
The file app/mem.h defines the following memory areas:
- Main heap for dynamic-style allocation (static pool)
- Flash swap buffer for firmware updates
- Camera frame buffers (2 buffers)
When functions require temporary memory that exceeds stack capacity, they must coordinate with called functions to avoid overlapping allocations. Each function is responsible for its entire memory segment. Camera buffers can be used for very large allocations when the device is NOT scanning QR codes.
The project uses an A/B firmware scheme with a data partition that extends across the two memory banks. See docs/FLASHMAP.md for detailed flash memory layout information.
The project uses FreeRTOS with static allocation. See app/FreeRTOSConfig.h for configuration details.
Tasks are defined using macros in app/common.h. See the task definition and creation macros for the complete pattern.
Task priorities are defined in app/main.c:
- USB task: priority 1
- Core task: priority 2
- UI task: priority 3
- Functions:
snake_case(e.g.,hal_camera_init,core_export_key) - Types:
snake_case_t(e.g.,hal_err_t,core_ctx_t) - Constants:
UPPER_CASE(e.g.,CAMERA_FB_SIZE,FW_MAJOR) - Globals:
g_prefix (e.g.,g_core,g_ui_cmd) - Static variables: No prefix (file-scope static)
Common macros and attributes are defined in app/common.h. Refer to that file for alignment, section placement, inlining, and weak symbol macros.
Functions return error codes:
HAL_SUCCESS/HAL_FAILfor HAL functionsERR_OK/ERR_XXXfor application functionsapp_err_tfor application-level errors
ARM inline assembly uses GCC syntax. See app/common.h for examples.
The HAL is defined in app/hal.h and provides:
- Camera: Initialization, start/stop, frame acquisition and submission
- Screen: Display initialization, pixel drawing, window management
- SmartCard: ISO 7816 communication (start/stop, send/recv, PPS)
- USB: HID communication (send, receive, stall control)
- Crypto: Hardware-accelerated operations (SHA256, CRC32, AES, ECDSA, bignum)
- Flash: Program, erase, and firmware switching
- GPIO/I2C/SPI: Peripheral control
- Timer/ADC/PWM: System utilities
The camera uses a double-buffered DMA approach. See stm32/Core/Src/stm32_camera.c for the implementation details.
QR scanning uses the camera in a dedicated task:
- Camera captures frames via DMA
- QR scan task processes frames using
quirclibrary - Decoded data is passed to UR (Unrestricted Resource) decoder
- CBOR data is deserialized for display/approval
Key files:
app/qrcode/qrscan.c- QR scanning logicapp/qrcode/qrcodegen.c- QR code generation (OpenMV port)
The project uses hardware acceleration where available:
- PKA: ECC operations (secp256k1, NIST P-256)
- HASH: SHA2/SHA3 hardware accelerator
- RNG: Hardware random number generator
- AES: Hardware AES accelerator
Software fallbacks are available via SOFT_ macros.
The smartcard interface communicates with a Keycard device. The Keycard protocol documentation is available at https://keycard.tech/en/developers/overview.
CRITICAL: Agents must NOT invent or create new APDU commands. All APDU commands must be defined in the existing Keycard protocol specification. Refer to the Keycard documentation and existing implementations in the codebase for the complete list of supported commands.
Key files:
app/iso7816/smartcard.c- Smartcard communication implementationapp/keycard/- Keycard protocol implementation
The project uses CMake with custom presets in CMakePresets.json.
Always use the following command to build:
cmake --build --preset releaseDo not use make, ninja, or any other build tool directly. Always go through CMake's preset system to ensure the correct toolchain and configuration are used.
- Never use malloc/free: All memory must be statically allocated
- Stack depth: Be careful with stack usage; use heap memory for large buffers
- Memory coordination: When using
g_mem_heap, coordinate with called functions - Camera buffers: Can be reused for large allocations when not scanning
- Nocache section: Use
APP_NOCACHEfor DMA buffers (e.g., screen framebuffer) - Aligned sections: Use
APP_ALIGNEDfor DMA and cache-line aligned data
Tasks communicate via:
- Task notifications: Fast one-way signaling (used for camera, screen, smartcard)
- Command queue: UI commands from core to UI task
- Global state:
g_core,g_ui_cmd, etc.
The project supports test mode via TEST_APP macro:
- In test mode, certain functions are accessible for testing
- Use
TEST_APP_ACCESSIBLEmacro for test-accessible declarations