Labels: security, bug, pro, priority:high
File: src/tether/pro/license.py:212 (version not in (LICENSE_VERSION, LICENSE_VERSION_LEGACY_UNSIGNED)), :222 (signature verified only when == LICENSE_VERSION)
Description. load_license accepts both v2 (signed) and v1 (legacy unsigned). For v1 it skips signature verification entirely and only logs a warning.
Why it matters. An attacker writes ~/.tether/pro.license with license_version=1, tier="enterprise", expires_at far in the future, signature="", and a hardware_binding matching their own host (obtainable via probe_hardware_binding). No private key required — the entire v2 Ed25519 chain is bypassed by selecting the legacy version. Classic signature-downgrade; unlocks all Pro entitlements for free.
Tasks.
Dedup. No issue/PR covers the v1 acceptance path. PR #229 preserves signed fields on heartbeat rewrite — different concern.
Labels:
security,bug,pro,priority:highFile:
src/tether/pro/license.py:212(version not in (LICENSE_VERSION, LICENSE_VERSION_LEGACY_UNSIGNED)),:222(signature verified only when== LICENSE_VERSION)Description.
load_licenseaccepts both v2 (signed) and v1 (legacy unsigned). For v1 it skips signature verification entirely and only logs a warning.Why it matters. An attacker writes
~/.tether/pro.licensewithlicense_version=1,tier="enterprise",expires_atfar in the future,signature="", and ahardware_bindingmatching their own host (obtainable viaprobe_hardware_binding). No private key required — the entire v2 Ed25519 chain is bypassed by selecting the legacy version. Classic signature-downgrade; unlocks all Pro entitlements for free.Tasks.
TETHER_DEV=1env flag.Dedup. No issue/PR covers the v1 acceptance path. PR #229 preserves signed fields on heartbeat rewrite — different concern.