Secure Runtime Policy
This document describes the runtime policy enforcement implemented in the UBI secure backend: freshness synchronisation, event callbacks, key-version refcount tracking, usage budgets, and the sticky crypto read-only mode.
Freshness Sync
After every commit-visible mutation (volume create/resize/remove, LEB write,
LEB map, PEB erase) the backend calls sync_freshness according to the
cadence configured by CONFIG_UBI_CRYPTO_FRESHNESS_SYNC_DELTA:
delta = 0 (default): sync after every mutation.
delta > 0: sync every N mutations.
If sync_freshness returns a non-zero error code, the backend:
Emits
UBI_CRYPTO_EVENT_FRESHNESS_SYNC_FAILUREvia theevent_cb.Optionally enters sticky crypto read-only when
CONFIG_UBI_CRYPTO_STRICT_RO_ON_FRESHNESS_SYNC_FAILURE=y.
Event Callback and Verdicts
Every security-relevant event is delivered through the application-provided
event_cb. The callback returns one of:
Verdict |
Meaning |
|---|---|
|
Normal operation continues. |
|
Sticky crypto read-only: all subsequent mutations are rejected with |
Event Types
Event |
Trigger |
|---|---|
|
AEAD authentication failed during LEB read (EC, VID, or data domain). |
|
Post-AEAD plaintext has valid authentication but invalid structure (size mismatch). |
|
On-flash object carries a key version absent from the runtime allowlist. |
|
|
|
On-flash freshness lags behind the trusted store at attach time. |
|
|
|
Platform RNG could not produce a fresh salt for a secure write. |
|
LEB write/byte budget crossed soft threshold ( |
|
LEB write/byte budget crossed hard threshold ( |
|
All on-flash PEBs authenticated with a non-write-active key version have been erased. The key material can be safely destroyed. |
Sticky Crypto Read-Only
When read_only_crypto is set (by an event callback verdict or by a strict-RO
Kconfig policy), the central mutation gate blocks all mutation classes:
UBI_MUT_RESERVED_METADATA(volume create/resize/remove)UBI_MUT_DATA_PATH(LEB write/map/unmap)UBI_MUT_MAINTENANCE(PEB erase)
The flag persists until ubi_device_deinit. It is independent of the degraded
read-only flag which only blocks reserved-metadata mutations.
Key-Version PEB Refcount
During attach, the init scan counts refcounts per data PEB: one for each EC header plus two for each VID-bearing PEB (VID header + LEB data record). Reserved PEB objects are excluded (small constant, no lifecycle pairing).
At runtime:
Write (VID commit): increment refcount by 2 for the write-active key version (VID header + LEB data objects).
Erase: decrement refcount for the old EC key version (×1), plus VID key version (×2) if the PEB had a VID header. Increment by 1 for the write-active key version (new EC header written after erase).
KEY_RETIRABLE: emitted when a non-write-active key version’s refcount reaches zero.
LEB Usage Budget
Each LEB write tracks:
leb_write_counter— number of AEAD encrypt operations per{key_version, volume_id}.leb_total_auth_bytes— cumulative authenticated bytes per{key_version, volume_id}.
Pre-write check (§14.2): before any flash mutation, the backend projects
the post-write counter and byte usage. If either would cross ROTATE_NOW_PCT,
the write is rejected with -ENOSPC and KEY_ROTATE_NOW is emitted. If the
48-bit nonce counter would overflow, the write is rejected with -EOVERFLOW.
Post-write check: after each successful LEB commit, usage percentages are
computed against the Kconfig budgets (UBI_CRYPTO_LEB_WRITE_BUDGET,
UBI_CRYPTO_LEB_TOTAL_AUTH_BYTES_BUDGET) and KEY_ROTATE_SOON or
KEY_ROTATE_NOW events are emitted when thresholds are crossed.
Error Propagation
Internal crypto error codes (UBI_SECURE_ENORAND, UBI_SECURE_ENOKEY,
UBI_SECURE_EFORMAT) propagate from low-level functions through the I/O
layer to callers that hold the ubi_device*. Those callers classify the
error and emit the appropriate event via the helpers in ubi_secure_event.h.
Read-Path Allowlist
During ubi_secure_leb_read, both the EC header and VID header key versions
are checked against the runtime allowed_key_versions policy. If either
key version is absent from the allowlist, the read is rejected with -EACCES
and KEY_VERSION_NOT_ALLOWLISTED is emitted.
Zeroization
All stack-local plaintext buffers (EC, VID, LEB decrypt outputs) and
heap-allocated scratch buffers are wiped via ubi_secure_zeroize() — a
volatile-qualified byte-by-byte memset that is not subject to dead-store
elimination — before returning or freeing.