Repository navigation
OTAmpy v4.6.0
·
166 commits
to main
since this release
Immutable
release. Only release title and notes can be modified.
OTAmpy 4.6.0 — 2026-09-03
Major (breaking changes)
- None. Every new setting below is opt-in: an existing device with an unchanged
configota.py, driven by a CLI with noOTAMPY_COMMAND_AUTH_KEY, behaves exactly as it did in 4.5.0.
Minor (functionality added in a backward compatible manner)
- Optional authentication for the runtime command surface. Until now, any station that could reach the device's UART could read, overwrite, delete or reboot it with no credential. On a wired link that is the same trust boundary as physical access; on a radio link — where OTAmpy is most useful — it means anyone in range.
- Setting
OTA_REQUIRE_AUTH = Trueon the device makes it require every command (RB,SR,UPDATE_REQUEST,RM,CAT,LS,MEM,RTC*,CP_*) to arrive asAUTH:<counter>:<hex-mac>:<command>. The host signs whenOTAMPY_COMMAND_AUTH_KEYis set. - The MAC is HMAC-SHA256 truncated to 8 bytes over a domain-separated, counter-prefixed copy of the command. The inner command is dispatched through the existing parser unchanged, so no command grammar moves.
- Counters are strictly increasing and seeded from the wall clock, so a command captured off the air cannot be replayed — including after a reboot, since an accepted
RB/SR/UPDATE_REQUESTsaves the counter as a boot floor before resetting. - The MAC sits above URST, so URST's retransmission is unchanged and unaware of it: a legitimate retry is never mistaken for a replay.
- New device settings:
OTA_REQUIRE_AUTH,COMMAND_AUTH_KEY,OTA_REPLAY_FLOOR_FILE. New host environment variables:OTAMPY_COMMAND_AUTH_KEY,OTAMPY_COUNTER_FILE. - Documented in
docs/protocol.mdsection 1.2, with all five settings shown commented out inconfigota.example.py.
- Setting
OTA_ALLOWED_PATH_PREFIXESoptionally restricts which pathsCP_START,CATandRMmay target. Unset, any non-traversing path is still allowed.
Patch (backward compatible bug fixes)
- Fixed an arbitrary file write in
CP_START(path traversal / persistent remote code execution).CP_START:<target>:<size>:<sha256>took its target straight from the command with no directory restriction and no..rejection — and since the SHA-256 field is attacker-supplied too, the checksum gate constrained nothing. A craftedCP_START:main.py:…(orboot.py, or anylib/path) overwrote device code, which then ran on the next reboot...is now always refused forCP_START,CATandRM, whether or not an allowlist is configured. There is no legitimate traversal on a filesystem rooted at/.- Prefix matching respects path-segment boundaries, so an allowed prefix of
datadoes not also admitdatabase.db. - The refusal happens before any directory or staging file is created, so a rejected copy leaves nothing behind.