One password, both transports
The device uses a single control password (factory default123456). BLE always
requires it. USB does not enforce it until the device is locked, which keeps
bench work convenient while allowing a fielded device to be secured.
factory-reset is the only remaining route.
info, state, storage and factory-reset respond regardless of lock state,
so a locked device still reports its identity and can always be recovered.
ef-cli info reports usb access and encryption, so a device that is refusing
a command indicates why.Authentication is per link and expires
Authenticate once and the remainder of that connection is authenticated. USB and BLE authenticate independently of each other. The grant is time-limited, because USB provides the device no means of detecting that the client which obtained it has disconnected. It ends at whichever occurs first: two minutes idle, ten minutes from the time it was granted (traffic does not extend this), a new authentication attempt on the same link, or a cable cycle.ef::Device repeats the handshake and retries transparently, so a long-lived
session is unaffected. A client implementing the wire protocol directly must
handle AUTH_REQUIRED on any gated verb and re-authenticate in response.
open() succeeds even with an incorrect password, because info, state and
storage are required by an operator recovering a device whose password has been
lost. Verify that gated calls will succeed before issuing one:
Opening a locked device for one session
Rather than supplying--password with every command, open the device for the
duration of the power session:
ef-cli info
reports LOCKED, open for this session rather than either state alone. The
command is refused on a device that is not locked, and a plain lock on from
either transport ends the session.
Authentication never applies this implicitly. A password authorizes one caller
for one call, whereas opening the device is a separate operator decision, since a
grant that extended from one client to every other client on the cable is the
behavior this replaced.
Encrypting recordings at rest
Recordings can be AES-256-GCM encrypted under a per-device key. The device generates the key itself and returns it once, at creation:key set is refused while a key already exists, and while the device is
recording. The reply names the key by ID and never returns the bytes.
--out refuses to overwrite an existing file rather than truncating it, because
the file it would replace may be the only copy of another device’s key.
Enabling encryption without a key is refused, so a session cannot report that it
is encrypting while recording unencrypted. Existing recordings retain the state
they were written with; the setting applies only to subsequent recordings.
ef-cli info reports the key ID, which is the first four bytes of its SHA-256
and never the key itself. The same ID is written into each recording header,
which identifies the saved key required to open a given file. On a locked device
the ID requires the password like any other gated field, since that value in a
file header would otherwise allow a nearby party to associate a device with
recordings in their possession. Whether a key exists is always readable.
record list marks each recording [encrypted] or [unencrypted] by reading
the container identifier from the file, so it reports the state on disk rather
than the current setting.
Reading an encrypted recording
Download the recording, then decrypt it on the host withef-decrypt:
download neither decrypts nor renames. Without a destination it writes
<name>.mcap regardless of the contents, so an encrypted recording is written to
a file that no MCAP reader will open unless a destination is specified, as above.
The key file is the one written by key show --out: 32 raw bytes, or 64 hex
characters with an optional trailing newline. ef-decrypt writes the plaintext
to the named output and its summary to stderr, allowing it to be used in a
pipeline.
A recording truncated by power loss returns exit
1 rather than indicating
corruption, and decrypts up to its last complete chunk. An incorrect key is
detected at the header rather than through repeated chunk failures, and the error
names the key_id the file requires.
ef-decrypt requires libcrypto (apt install libssl-dev) and is omitted by the
build when it is unavailable.
Destroying the key
Two commands destroy the key, and both are irreversible for every recording written under it, including copies already transferred elsewhere.key_id to match the installed key, which prevents an SDK
caller from destroying a key it has not identified, and it is refused unless the
device is IDLE: a running session holds the key in memory and would otherwise
continue writing under a key the device had reported as destroyed.
Rotation is delete followed by create, as two deliberate steps. create is
refused while a key exists, because replacing one would render every recording
written under it permanently undecryptable.
Recovering a lost password
Reset the device over USB, where physical possession serves as the credential:encryption delete, the reset does not return the key before destroying
it. It responds unauthenticated over USB, and returning a key to an
unauthenticated caller is the exposure that destroying it is intended to remove.
Over BLE it requires the password like any other verb, since an unauthenticated
remote reset would allow a nearby party to destroy the key for every recording.
The key is held in the device’s writable storage, so a full reflash destroys it
as a reset does. A saved copy is the only form that survives either.
Scripting these commands
encryption delete --confirm and factory-reset prompt for typed confirmation
on a terminal. Without a terminal (ssh box 'ef-cli ...', cron, redirected
stdin) they refuse rather than prompt, so pass --yes to confirm in a script:
From the library
get_device_information(): usb_locked,
session_unlocked, encryption_enabled, encryption_key_present,
encryption_key_id and encryption_algorithm. See the
API reference for the full contracts.
