========================== 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 :doc:`/trustzone/secure-world`, :doc:`/trustzone/crypto-engine` and :doc:`/abl/verified-boot`. 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. 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 :doc:`/trustzone/secure-world`. The mechanism below this — PAS authentication of the trustlet images — is the same one the DSP/GPU images use (:doc:`firmware-blobs`). Keys, credentials and attestation ================================= - **Keymaster** — ``android.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 keybox** — ``init.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. - **RPMB** — ``init.rpmb.sh`` sets up the Replay-Protected Memory Block on the storage device (:doc:`/trustzone/secure-world`), which Keymaster/Gatekeeper use for rollback-protected counters and secure state. - **Gatekeeper** — ``android.hardware.gatekeeper@1.0-service-qti`` + ``gatekeeperd``; PIN/pattern/password enrolment and verification with TZ-enforced throttling (the HMAC key never leaves TZ). 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.bin`` — ``qsee_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: .. code-block:: text 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. ``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, :doc:`/display/tcon`), ``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, :doc:`apk-sweep-findings`; 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 (:doc:`/edl/entry`); 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). 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 (:doc:`/abl/verified-boot`) — all three must be considered for a real unlock/flash story. - **Test-Harness-Mode region** (``0x7d507``–``0x7fc16``) — all-zero: not in use. Storage encryption (vold / FBE) =============================== ``/data`` (f2fs) is **File-Based Encryption v2**, per ``fstab.default``: .. code-block:: text 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 (:doc:`/soc/storage`, :doc:`/trustzone/crypto-engine`). ``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 :doc:`/soc/storage`. DRM (Widevine, ClearKey, CAS) ============================= - **Widevine** — ``android.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, :doc:`firmware-blobs`). 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. - **ClearKey** — ``mediadrm/libdrmclearkeyplugin.so``, the software test plugin. - **CAS** — ``android.hardware.cas@1.2-service`` (Conditional Access System, for broadcast/streaming; pairs with the ``mpq-*`` demux modules in :doc:`drivers-hals`, largely vestigial here). - Framework: ``libmediadrm.so`` / ``libdrmframework.so`` and the metrics libraries in ``/system/lib64``. 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 :doc:`firmware-blobs`. Template storage and matching run in a TZ trustlet; the sensor's IRQ/reset/SPI-CS pins are in :doc:`/soc/pinctrl`. 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 (:doc:`/wireless/hal`), the eSE is present in the reference stack but not exercised here. 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, :doc:`/trustzone/secure-world`) or key attestation, FBE unlock and DRM all fail. The current chain is test-key signed with secure boot un-fused (:doc:`/abl/verified-boot`, :doc:`/boot/secure-boot`), which is what makes replacing the bootloader feasible without bricking these services — but the trustlets themselves remain signature-checked by TZ. 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: :doc:`/trustzone/secure-world` (TZ, QSEE trustlets, HYP), :doc:`/trustzone/crypto-engine` (QCE/ICE/HWKM hardware), :doc:`/soc/storage` (ICE inline crypto), :doc:`/abl/verified-boot` (fuses, secure-boot state), :doc:`/boot/secure-boot` (verified boot), :doc:`firmware-blobs` (widevine/fingerprint firmware, trustlet load path), :doc:`drivers-hals` (eSE, HAL-layer overview), :doc:`/sensors/hal` (fingerprint impls), :doc:`/wireless/hal` (NFC).