12.3. Security and DRM userspace

The device’s trust model has two parts. The secure world — TrustZone (QSEE), the hypervisor, the trustlets and the fused keys — is below userspace and is documented in Secure world and coprocessors (TZ, HYP, trustlets), Crypto engine (QCE) and hardware RNG and AVB enforcement code path (LoadImageAndAuth / libavb). This page is the userspace half: the HAL services and daemons that reach into TZ, the key/credential stores, the storage-encryption plumbing, and the DRM stack. None of it is the root of trust — it is the set of clients that depend on the root of trust — but it is what a replacement bootloader has to keep working, because keystore attestation, file-based encryption and Widevine all break if the TZ/keymaster/RPMB chain underneath them is disturbed.

Everything here is read statically from system_a.img / vendor_a.img and artifacts/boot_a/fstab.default. No code was executed.

12.3.1. The bridge to TrustZone (QSEE)

Every hardware-backed security service in userspace reaches its trustlet through the QSEECOM channel:

  • qseecomd — the daemon that loads and talks to QSEE trustlets;

  • libQSEEComAPI.so + vendor.qti.hardware.qseecom@1.0-service / …-impl.so — the API and HAL wrapper;

  • vendor.qti.hardware.qteeconnector@1.0-service — the newer QTEE connector;

  • vendor.qti.hardware.tui_comm@1.0-service-qti — the Trusted UI channel (secure display/touch, e.g. for PIN entry).

The trustlets these load (keymaster, widevine, gatekeeper, the fingerprint TA, soter …) live in the secure world and are catalogued in Secure world and coprocessors (TZ, HYP, trustlets). The mechanism below this — PAS authentication of the trustlet images — is the same one the DSP/GPU images use (Firmware-blob inventory (the payloads)).

12.3.2. Keys, credentials and attestation

  • Keymasterandroid.hardware.keymaster@4.0-service-qti and @4.1-service-qti (backed by libqtikeymaster4.so); the private keys live in the TZ Keymaster trustlet, not in Linux. keystore2 + android.system.keystore2 and lib_android_keymaster_keymint_utils.so are the framework front-end. Provisioning helpers: libkeymasterprovision.so, libkeymasterdeviceutils.so.

  • Attestation keyboxinit.keybox.sh / init.erase_keybox.sh provision (and wipe) the device attestation keybox that Keymaster uses to sign key attestations; the keys are sealed to the device.

  • RPMBinit.rpmb.sh sets up the Replay-Protected Memory Block on the storage device (Secure world and coprocessors (TZ, HYP, trustlets)), which Keymaster/Gatekeeper use for rollback-protected counters and secure state.

  • Gatekeeperandroid.hardware.gatekeeper@1.0-service-qti + gatekeeperd; PIN/pattern/password enrolment and verification with TZ-enforced throttling (the HMAC key never leaves TZ).

12.3.3. QSEE secure storage: keystore.bin and persist/data

Checked directly against _READONLY/lun0/keystore.bin and _READONLY/lun0/persist.bin (raw dumps, not the extracted images used elsewhere on this page); full transcript and command-by-command evidence in artifacts/re_static/item_qsee_storage_recheck.txt.

keystore.bin (the dedicated 512 KiB GPT partition labelled keystore in rawprogram0.xml) is entirely zero bytes — all 524288 bytes, confirmed by full-file scan. It is not encrypted or opaque; it is empty/unprovisioned on this unit (or wiped before the dump — the two are indistinguishable statically). Whatever holds live key material, it isn’t this partition.

persist.bin is a normal ext4 filesystem (confirmed with 7z l, matching the pre-existing artifacts/re_eink/persist_all extraction). Under data/ it holds two directories with opaque, hash-like base64url names — this is Qualcomm’s QSEE SFS (Secure File System): keymaster asks to open a plaintext path like /persist/data/rsa_attest_key and QSEE’s qsee_sfs_* API (confirmed present by name in keymaster_a.binqsee_sfs_open, qsee_sfs_read, qsee_sfs_write, sfs_rpmb_set/get, alongside the literal strings /persist/data/rsa_attest_key and /persist/data/ecc_attest_key) transparently maps it to an opaque on-disk name before it ever touches flash. That name-obfuscation mapping could not be reversed (the key/digest that produces it isn’t a static constant in the TA).

What was recovered, byte-for-byte, is the container format wrapping every leaf file in those directories:

offset  size  field
0x00    32    opaque tag (MAC/digest-shaped; differs every rewrite)
0x20    4     LE u32 — per-file RPMB-backed generation/anti-rollback counter
              (proven, not guessed: every "live" file's counter is exactly
              one more than its own ".bak" twin's)
0x24    var   NUL-terminated on-disk (obfuscated) filename
...     var   NUL-terminated CLEARTEXT logical name/path
...     0     zero-padded out to a fixed offset 0x79
0x79    var   payload — genuinely high-entropy ciphertext (fails
              ``openssl asn1parse``, no readable ``strings``, re-randomized
              on every rewrite down to the trailing 4 KiB page)

Because the logical name field is cleartext, the one attestation-id SFS context on this device (data/[REDACTED unique per physical unit]/) directly reveals its own inventory without needing the payload: six files logically named brand / device / product / serial / manufacturer / model (the device-ID attestation tags from artifacts/re_static/item2_keymaster_interface.txt section [F]), one attest_key (12692 bytes — the RSA/ECC attest key blob), and one gk_sfs_file (gatekeeper’s SFS-backed state). A second SFS context (data/[REDACTED unique per physical unit]/) holds a widevine file whose logical name is stored as a full path, /persist/data/widevine/master_gen_num.dat — confirming the same container format and the same qsee_sfs API is shared by the DRM trustlet, not exclusive to keymaster.

The payload ciphertext (post-0x79) and the name-obfuscation key are genuinely unreachable from static analysis. The container header, however, is cleartext and fully mapped, and positively identifies what is stored (all six attestation-id fields, the attest key, gatekeeper state, and a widevine counter): it is provisioned, present, and inventoried; only the payload bytes resist reading.

12.3.4. persist and metadata: the rest of the inventory

persist.bin and metadata.bin carry more than the QSEE SFS blobs above; the rest of their content, for completeness:

  • ``persist``’s other populated directories: sensors/ — IMU factory calibration (accel/gyro fac_cal bias + correction matrix, mag_cal, placement) plus a sensors/registry/; data/pfm/licenses/; time/ (ats time anchors); hvdcp_opti/ (battery state-of-health); rfs/ (remote-filesystem server stubs). Empty on this unit: display/ (no EPD calibration stored here — the panel VCOM lives in onyxconfig and the FPGA flash instead, E-ink driver internals (register-level, from the kernel)), secnvm, iar_db, vpp, lost+found. Not here: the WLAN MAC override is /vendor/firmware/wlan/qca_cld/wlan_mac.bin (32 B), not in persist.

  • ``metadata`` (16 MiB ext4, ``Last Mounted = /metadata``) — the pre-decryption partition FBE needs before /data unlocks. Populated: vold/metadata_encryption/key/ holds a real, Keymaster-wrapped metadata key (encrypted_key 228 B, keymaster_key_blob 228 B — the key material never leaves the TA, per the FBE story below), secdiscardable (16 KiB, the auxiliary secret vold XORs in for “secure discard”), and version ("1"). This confirms metadata encryption is provisioned, not merely declared in fstab. Also present: gsi/dsu/avb/{q,r,s}-gsi.avbpubkey — three 1032-byte AVB public keys used by libfs_mgr/init to verify a Dynamic-System-Update generic system image before mounting it over /system (the on-partition counterpart to the OEM-unlock/DSU installer presence confirmed app-side, APK sweep: bulk decompile of the remaining 123 system/product apps; not diffed against upstream AOSP’s published copies — DSU keys are meant to be identical on every certified device); bootstat/persist.sys.boot.reason reading reboot,edl — the backup this dump came from was pulled via a cold boot into 9008/Sahara EDL mode (EDL / 9008 entry and the deep-flash cable); and empty-but-provisioned aconfig/, apex/sessions/, gsi/{ota,remount}, ota/snapshots, staged-install, userspacereboot, password_slots directories (machinery present, never fired on this unit).

12.3.5. Factory Reset Protection — frp

frp.bin (LUN0, 512 KB) is 99.98% zero but not blank: a real, live Android PersistentDataBlockService partition, decoded field-for-field against the AOSP source (services/core/java/com/android/server/pdb/PersistentDataBlockService.java) and verified byte-exact:

  • Header — a 32-byte digest at +0x0, the 4-byte marker PARTITION_TYPE_MARKER = 0x19901873 at +0x20, then a 4-byte totalDataSize field at +0x24 reading 0 — the plain FRP data block (OEM-unlock / persist.sys.locale etc.) is present-but-empty.

  • ``FRP_SECRET_MAGIC`` (da c2 fc cd b9 1b 09 88) found byte-exact at its AOSP-computed offset, 0x7d4df — independently confirming both the constants and the partition’s real-world layout. This is the newer “FRP secret” deactivation mechanism (Android 14+), confirmed present/enabled.

  • FRP credential region (0x7fc17) — not empty: a 4-byte big-endian size header reading 0x67 (103) followed by real payload bytes — a credential handle is stored here (written whenever Setup Wizard is completed with an account). By AOSP design this block survives a normal Settings Factory reset (that’s the whole point of FRP — a thief can’t clear it that way), so its presence is consistent with standard factory/QC provisioning-and-reset during manufacturing, not necessarily any use after purchase. The credential’s actual content (account identity) is opaque and was not decoded here — only the region’s occupied/empty state was read.

  • OEM-unlock byte — the literal last byte of the partition (0x7ffff) reads 0 — OEM unlock is not marked allowed through this mechanism. This is a different, higher-level gate than the QFPROM SEC_BOOT fuse or the ABL device_info.is_unlocked bit (AVB enforcement code path (LoadImageAndAuth / libavb)) — all three must be considered for a real unlock/flash story.

  • Test-Harness-Mode region (0x7d5070x7fc16) — all-zero: not in use.

12.3.6. Storage encryption (vold / FBE)

/data (f2fs) is File-Based Encryption v2, per fstab.default:

fileencryption=aes-256-xts:aes-256-cts:v2+inlinecrypt_optimized+wrappedkey_v0
metadata_encryption=aes-256-xts:wrappedkey_v0
keydirectory=/metadata/vold/metadata_encryption

The important flags are wrappedkey_v0 and inlinecrypt: file contents are AES-256-XTS and filenames AES-256-CTS, but the keys are hardware-wrapped (HWKM) and used through inline crypto — i.e. the ICE engine inside the UFS controller, not a software cipher (Storage controllers — UFS, ICE, and SDCC, Crypto engine (QCE) and hardware RNG). vold obtains the class keys via Keymaster, hands the wrapped key to the block-crypto/ICE path, and the raw key never appears in the kernel. Metadata for the /metadata partition is separately encrypted with a wrapped key. Removable storage (the microSD /storage/sdcard1) uses the legacy encryptable=footer scheme instead.

This is the userspace end of the crypto chain whose hardware end (SCSI_UFS_CRYPTO_QTI / HWKM) is in Storage controllers — UFS, ICE, and SDCC.

12.3.7. DRM (Widevine, ClearKey, CAS)

  • Widevineandroid.hardware.drm@1.3-service.widevine with lib64/libwvhidl.so and, crucially, a vendor liboemcrypto.so: the OEMCrypto implementation runs against the TZ Widevine trustlet (the widevine firmware blob is on the NON-HLOS partition, Firmware-blob inventory (the payloads)). A hardware OEMCrypto + TZ trustlet is the configuration required for Widevine L1 (keys and decrypt confined to the secure world / ICE); the level a given build is actually provisioned at is a runtime/keybox property, not visible statically.

  • ClearKeymediadrm/libdrmclearkeyplugin.so, the software test plugin.

  • CASandroid.hardware.cas@1.2-service (Conditional Access System, for broadcast/streaming; pairs with the mpq-* demux modules in Vendor drivers and HALs, largely vestigial here).

  • Framework: libmediadrm.so / libdrmframework.so and the metrics libraries in /system/lib64.

12.3.8. Biometrics (fingerprint)

Two HAL services ship — android.hardware.biometrics.fingerprint@2.1-service and Onyx’s vendor.fptool.fingerprint@2.0-service — over three vendor implementations (chipsailing.fingerprint, fsfingerprint, libmicroarray), matching the three fingerprint firmware sets in Firmware-blob inventory (the payloads). Template storage and matching run in a TZ trustlet; the sensor’s IRQ/reset/SPI-CS pins are in Pin control — TLMM and the PMIC GPIOs.

12.3.9. Other secure services

  • vendor.qti.hardware.soter@1.0-service — Tencent Soter payment attestation (a TZ-backed key-attestation scheme used by Chinese payment apps).

  • vendor.qti.esepowermanager@1.1-service — embedded Secure Element power management; since NFC is declared unavailable (Wireless drivers and HAL), the eSE is present in the reference stack but not exercised here.

12.3.10. Relevance below userspace

Keymaster/Gatekeeper keys are sealed by TZ using fused key material, FBE class keys are unwrapped by HWKM/ICE, Widevine decryption happens inside TZ, and RPMB provides rollback protection — the entire chain above is anchored in TZ and RPMB. A custom bootloader that replaces ABL/XBL must preserve the TZ, keymaster and RPMB provisioning (and the devcfg that configures them, Secure world and coprocessors (TZ, HYP, trustlets)) or key attestation, FBE unlock and DRM all fail. The current chain is test-key signed with secure boot un-fused (AVB enforcement code path (LoadImageAndAuth / libavb), Secure boot and image signing), which is what makes replacing the bootloader feasible without bricking these services — but the trustlets themselves remain signature-checked by TZ.

12.3.11. Provenance

Source:

artifacts/super/vendor_a.img (bin/hw + lib64/hw security/DRM HAL services and impls, liboemcrypto.so, libwvhidl.so, libqtikeymaster4.so, libQSEEComAPI.so, qseecomd, init.{keybox, rpmb,erase_keybox}.sh), system_a.img (keystore2, gatekeeperd, libmediadrm/libdrmframework, keystore2/keymaster/gatekeeper NDK libs), artifacts/boot_a/fstab.default (FBE / metadata-encryption flags), _READONLY/lun0/keystore.bin and _READONLY/lun0/persist.bin (raw partition dumps, cross-checked against the existing artifacts/re_eink/persist_all extraction), _READONLY/lun4/keymaster_a.bin (qsee_sfs_* / sfs_rpmb_* strings) — see artifacts/re_static/item_qsee_storage_recheck.txt and artifacts/re_static/item2_keymaster_interface.txt for the full transcripts.

Method:

ext4 images listed/extracted read-only with 7z; encryption flags read from the fstab. keystore.bin/persist.bin inspected with file, 7z l, xxd, strings and a byte-level structure/entropy scan (python3); openssl asn1parse tried against candidate key blobs. No code executed; no keys or secure state accessed; no encryption defeated.

Cross-refs:

Secure world and coprocessors (TZ, HYP, trustlets) (TZ, QSEE trustlets, HYP), Crypto engine (QCE) and hardware RNG (QCE/ICE/HWKM hardware), Storage controllers — UFS, ICE, and SDCC (ICE inline crypto), AVB enforcement code path (LoadImageAndAuth / libavb) (fuses, secure-boot state), Secure boot and image signing (verified boot), Firmware-blob inventory (the payloads) (widevine/fingerprint firmware, trustlet load path), Vendor drivers and HALs (eSE, HAL-layer overview), Sensor drivers and HAL (fingerprint impls), Wireless drivers and HAL (NFC).