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:

  1. Emits UBI_CRYPTO_EVENT_FRESHNESS_SYNC_FAILURE via the event_cb.

  2. 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

UBI_CRYPTO_EVENT_CONTINUE

Normal operation continues.

UBI_CRYPTO_EVENT_ENTER_READ_ONLY

Sticky crypto read-only: all subsequent mutations are rejected with -EROFS. Reads remain functional.

Event Types

Event

Trigger

AUTH_FAILURE

AEAD authentication failed during LEB read (EC, VID, or data domain).

FORMAT_VIOLATION

Post-AEAD plaintext has valid authentication but invalid structure (size mismatch).

KEY_VERSION_NOT_ALLOWLISTED

On-flash object carries a key version absent from the runtime allowlist.

KEY_VERSION_UNAVAILABLE

get_key_id callback failed — key material not available for a key version.

ROLLBACK_POLICY_MISMATCH

On-flash freshness lags behind the trusted store at attach time.

FRESHNESS_SYNC_FAILURE

sync_freshness callback returned non-zero.

RNG_FAILURE

Platform RNG could not produce a fresh salt for a secure write.

KEY_ROTATE_SOON

LEB write/byte budget crossed soft threshold (ROTATE_SOON_PCT, default 80%).

KEY_ROTATE_NOW

LEB write/byte budget crossed hard threshold (ROTATE_NOW_PCT, default 95%).

KEY_RETIRABLE

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.