sycamore_row_5G: checkpoint V18 stock TEE identity

This commit is contained in:
Nicholas Andrew 2026-08-27 05:56:11 -04:00
commit cfb7c712c9
275 changed files with 3939 additions and 0 deletions

68
Android.mk Normal file
View file

@ -0,0 +1,68 @@
LOCAL_PATH := $(call my-dir)
ifeq ($(TARGET_DEVICE),sycamore_row_5G)
define sycamore-recovery-binary
include $$(CLEAR_VARS)
LOCAL_MODULE := $(1)
LOCAL_MODULE_STEM := $(2)
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MODULE_PATH := $$(TARGET_RECOVERY_ROOT_OUT)/$(3)
LOCAL_SRC_FILES := recovery/root/$(3)/$(2)
LOCAL_MODULE_TAGS := optional
LOCAL_CHECK_ELF_FILES := false
LOCAL_STRIP_MODULE := false
include $$(BUILD_PREBUILT)
endef
define sycamore-recovery-library
include $$(CLEAR_VARS)
LOCAL_MODULE := $(1)
LOCAL_MODULE_STEM := $(2)
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_PATH := $$(TARGET_RECOVERY_ROOT_OUT)/$(3)
LOCAL_SRC_FILES := recovery/root/$(3)/$(2)
LOCAL_MODULE_TAGS := optional
LOCAL_CHECK_ELF_FILES := false
LOCAL_STRIP_MODULE := false
include $$(BUILD_PREBUILT)
endef
$(eval $(call sycamore-recovery-binary,sycamore_teei_daemon,teei_daemon,vendor/bin))
$(eval $(call sycamore-recovery-binary,sycamore_keymint_service,android.hardware.security.keymint@2.0-service.beanpod,vendor/bin/hw))
$(eval $(call sycamore-recovery-binary,sycamore_gatekeeper_service,android.hardware.gatekeeper@1.0-service,vendor/bin/hw))
$(eval $(call sycamore-recovery-library,sycamore_keymint_ndk_v2,android.hardware.security.keymint-V2-ndk.so,vendor/lib64))
$(eval $(call sycamore-recovery-library,sycamore_secureclock_ndk_v1,android.hardware.security.secureclock-V1-ndk.so,vendor/lib64))
$(eval $(call sycamore-recovery-library,sycamore_sharedsecret_ndk_v1,android.hardware.security.sharedsecret-V1-ndk.so,vendor/lib64))
$(eval $(call sycamore-recovery-library,sycamore_tee_common,libTEECommon.so,vendor/lib64))
$(eval $(call sycamore-recovery-library,sycamore_tee_imsg_log,libimsg_log.so,vendor/lib64))
$(eval $(call sycamore-recovery-library,sycamore_tee_vfs,libteei_daemon_vfs.so,vendor/lib64))
$(eval $(call sycamore-recovery-library,sycamore_gatekeeper_impl,android.hardware.gatekeeper@1.0-impl.so,vendor/lib64/hw))
$(eval $(call sycamore-recovery-library,sycamore_gatekeeper_tee,gatekeeper.beanpod.so,vendor/lib64/hw))
$(eval $(call sycamore-recovery-library,sycamore_gatekeeper_soft,libSoftGatekeeper.so,vendor/lib64/hw))
$(eval $(call sycamore-recovery-library,sycamore_gatekeeper_hidl,android.hardware.gatekeeper@1.0.so,vendor/lib64))
# Keep the stock Android 16 private KeyMint C++ ABI family coherent. These are
# selected only by the KeyMint/Gatekeeper service-specific LD_LIBRARY_PATH.
$(eval $(call sycamore-recovery-library,sycamore_keymint_private_utils,lib_android_keymaster_keymint_utils.so,vendor/lib64))
$(eval $(call sycamore-recovery-library,sycamore_keymaster_messages,libkeymaster_messages.so,vendor/lib64))
$(eval $(call sycamore-recovery-library,sycamore_keymaster_portable,libkeymaster_portable.so,vendor/lib64))
$(eval $(call sycamore-recovery-library,sycamore_keymint_private,libkeymint.so,vendor/lib64))
$(eval $(call sycamore-recovery-library,sycamore_puresoft_keymaster,libpuresoftkeymasterdevice.so,vendor/lib64))
$(eval $(call sycamore-recovery-library,sycamore_soft_attestation,libsoft_attestation_cert.so,vendor/lib64))
$(eval $(call sycamore-recovery-library,sycamore_cppbor_external,libcppbor_external.so,vendor/lib64))
$(eval $(call sycamore-recovery-library,sycamore_cppcose_rkp,libcppcose_rkp.so,vendor/lib64))
# Diagnostic V8 only: observe the 64-byte STORAGE_KEY at Beanpod's final
# userspace boundary, then tail-call the byte-identical stock A16 function.
include $(CLEAR_VARS)
LOCAL_MODULE := libsycamore_keytrace
LOCAL_SRC_FILES := diagnostics/keytrace.cpp
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/vendor/lib64
LOCAL_MODULE_TAGS := optional
LOCAL_SHARED_LIBRARIES := libcrypto libdl liblog
include $(BUILD_SHARED_LIBRARY)
endif

5
AndroidProducts.mk Normal file
View file

@ -0,0 +1,5 @@
PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/twrp_sycamore_row_5G.mk
COMMON_LUNCH_CHOICES := \
twrp_sycamore_row_5G-eng

162
BoardConfig.mk Normal file
View file

@ -0,0 +1,162 @@
DEVICE_PATH := device/motorola/sycamore_row_5G
# Architecture (64-bit recovery; runtime CPU properties identify Cortex-A55/A76 cores).
TARGET_ARCH := arm64
TARGET_ARCH_VARIANT := armv8-a
TARGET_CPU_ABI := arm64-v8a
TARGET_CPU_ABI2 :=
TARGET_CPU_VARIANT := generic
TARGET_CPU_VARIANT_RUNTIME := cortex-a55
TARGET_USES_64_BIT_BINDER := true
# Platform and assertions.
TARGET_BOARD_PLATFORM := mt6835
TARGET_BOOTLOADER_BOARD_NAME := sycamore_row_5G
TARGET_NO_BOOTLOADER := true
TARGET_OTA_ASSERT_DEVICE := sycamore_row_5G,XT2575-4
# Stock first API level is 35, but twrp-12.1 is an API 32 build and rejects a
# BOARD_SHIPPING_API_LEVEL newer than its platform SDK. Keep the measured value
# in the bring-up log rather than emitting a false API-32 build property.
# The stock Beanpod KeyMint service configures its TA from these properties.
# Pristine A16 vendor_boot supplies 2026-07-05 for both, and the live metadata
# key blob is bound to OS patch level 202607. Leaving the TWRP 12.1 defaults
# (202204 / empty) makes secure world reject that newer blob as a downgrade.
# These are exact stock inputs, not the old twrpdtgen 2099 bypass.
PLATFORM_SECURITY_PATCH := 2026-07-05
VENDOR_SECURITY_PATCH := 2026-07-05
# Exact factory kernel, DTB, and vendor_boot v4 header addresses. These values
# are mechanically extracted from the pristine ZUI 17.5 vendor_boot image.
TARGET_PREBUILT_KERNEL := $(DEVICE_PATH)/prebuilt/kernel
BOARD_INCLUDE_DTB_IN_BOOTIMG := true
BOARD_PREBUILT_DTBIMAGE_DIR := $(DEVICE_PATH)/prebuilt/dtb
BOARD_BOOT_HEADER_VERSION := 4
BOARD_KERNEL_PAGESIZE := 4096
BOARD_KERNEL_BASE := 0x40000000
BOARD_RAMDISK_USE_LZ4 := true
BOARD_KERNEL_SEPARATED_DTBO := true
BOARD_MKBOOTIMG_ARGS += --header_version $(BOARD_BOOT_HEADER_VERSION)
BOARD_MKBOOTIMG_ARGS += --kernel_offset 0x00000000
BOARD_MKBOOTIMG_ARGS += --ramdisk_offset 0x26f00000
BOARD_MKBOOTIMG_ARGS += --tags_offset 0x07c80000
BOARD_MKBOOTIMG_ARGS += --dtb_offset 0x07c80000
BOARD_MKBOOTIMG_ARGS += --vendor_cmdline "bootopt=64S3,32N2,64N2"
# Preserve the pristine stock vendor ramdisk byte-for-byte as the unnamed base
# PLATFORM ramdisk. AOSP then appends the generated TWRP RECOVERY fragment.
# This matches the factory vendor_boot v4 layout and preserves overlay ordering.
BOARD_PREBUILT_VENDOR_RAMDISK := \
$(DEVICE_PATH)/prebuilt/vendor_ramdisk/platform.cpio.lz4
# Physical partition sizes measured from GPT/factory images.
BOARD_BOOTIMAGE_PARTITION_SIZE := 67108864
BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE := 67108864
BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE := 8388608
BOARD_DTBOIMG_PARTITION_SIZE := 8388608
BOARD_PREBUILT_DTBOIMAGE := $(DEVICE_PATH)/prebuilt/dtbo.img
# Live liblp metadata: v10.2, 64 KiB max metadata, three slots, virtual A/B.
BOARD_SUPER_PARTITION_SIZE := 11811160064
BOARD_SUPER_PARTITION_GROUPS := main_dynamic_partitions
BOARD_MAIN_DYNAMIC_PARTITIONS_SIZE := 11809062912
# API-32 build/make cannot generate system_dlkm. It remains in recovery.fstab
# and live liblp metadata; this recovery-only target does not build super.
BOARD_MAIN_DYNAMIC_PARTITIONS_PARTITION_LIST := \
odm_dlkm product system system_ext vendor vendor_dlkm
TARGET_COPY_OUT_SYSTEM_EXT := system_ext
TARGET_COPY_OUT_PRODUCT := product
TARGET_COPY_OUT_VENDOR := vendor
TARGET_COPY_OUT_VENDOR_DLKM := vendor_dlkm
TARGET_COPY_OUT_ODM_DLKM := odm_dlkm
BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE := erofs
BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := erofs
BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := erofs
BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := erofs
BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE := erofs
BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE := erofs
BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE := f2fs
TARGET_USERIMAGES_USE_EXT4 := true
TARGET_USERIMAGES_USE_F2FS := true
# Recovery lives in vendor_boot. AOSP build/make emits a v4 RECOVERY ramdisk
# fragment when both switches below are true; stock's platform fragment already
# proves that all recovery resources belong in vendor_boot on this device.
TARGET_NO_RECOVERY := true
TW_HAS_NO_RECOVERY_PARTITION := true
BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE := true
BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT := true
BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT := true
TARGET_RECOVERY_FSTAB := $(DEVICE_PATH)/recovery.fstab
TARGET_RECOVERY_PIXEL_FORMAT := RGBX_8888
BOARD_HAS_NO_SELECT_BUTTON := true
# V18: exact production teei_daemon domain and narrowly scoped Microtrust
# device labels. The platform policy already defines tee/tee_exec and its
# init transition; this directory supplies only Sycamore-specific paths/types.
BOARD_VENDOR_SEPOLICY_DIRS += $(DEVICE_PATH)/sepolicy/vendor
# Display values measured from Android.
TARGET_SCREEN_WIDTH := 1600
TARGET_SCREEN_HEIGHT := 2560
TARGET_SCREEN_DENSITY := 320
TW_THEME := portrait_hdpi
TW_FRAMERATE := 90
# A/B, dynamic partitions, fastbootd and storage.
AB_OTA_UPDATER := true
TW_INCLUDE_FASTBOOTD := true
TW_INTERNAL_STORAGE_PATH := "/data/media/0"
TW_INTERNAL_STORAGE_MOUNT_POINT := "data"
TW_BACKUP_DATA_MEDIA := true
TW_USB_STORAGE := false
# Runtime diagnostic: the initial configfs ADB gadget works, but entering the
# GUI makes TWRP request mtp,adb. This tree has no configfs mtp,adb property
# action, so that transition unbinds the UDC and loses all host enumeration.
# Keep the proven adb-only gadget until MTK configfs MTP is implemented.
TW_EXCLUDE_MTP := true
# Tree-only FBE diagnostic: avoid twrpApex::loadApexImage(), whose TWRP 12.1
# implementation seeks an already-closed payload fd. Recovery's own crypto
# binaries and libraries are packaged in the ramdisk, so first prove the stock
# KeyMint/TEE chain without depending on Android's runtime APEX mounts. The
# generic source correction is retained separately as patch 0002.
TW_EXCLUDE_APEX := true
# Stock FBE v2 + metadata encryption. Branch must provide Android 15/16 crypto.
# Temporary first-hardware-boot diagnostic: keep /data encrypted and unmounted,
# and bypass the synchronous Android 16 metadata/FBE decrypt path so the GUI
# can start. Re-enable these only after the KeyMint/Keystore2 dependency chain
# is made compatible with the stock Android 16 vendor environment.
SYCAMORE_DIAGNOSTIC_NO_DECRYPT := false
ifneq ($(SYCAMORE_DIAGNOSTIC_NO_DECRYPT),true)
TW_INCLUDE_CRYPTO := true
TW_INCLUDE_CRYPTO_FBE := true
TW_INCLUDE_FBE_METADATA_DECRYPT := true
endif
BOARD_USES_METADATA_PARTITION := true
# Bring-up/debug essentials; no missing-dependency escape hatch.
TARGET_USES_LOGD := true
TWRP_INCLUDE_LOGCAT := true
TW_INCLUDE_REPACKTOOLS := true
TW_INCLUDE_RESETPROP := true
TW_INCLUDE_LIBRESETPROP := true
TW_EXCLUDE_DEFAULT_USB_INIT := true
TW_DEFAULT_LANGUAGE := en
TW_EXTRA_LANGUAGES := true
# Preserve the exact factory vendor_boot AVB fingerprint independently of the
# recovery build fingerprint.
BOARD_AVB_VENDOR_BOOT_FINGERPRINT := Motorola/XT2575-4/XT2575-4:15/AP3A.240905.015.A2/9bdeac_017:user/release-keys
# Match the stock algorithm-NONE vendor_boot hash-footer design. The salt is
# extracted from the pristine footer; no signing key is used or invented.
BOARD_AVB_ENABLE := true
BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS += \
--salt 11503e95ee971adc6c444e3ae47f564f178032ee9bbcf99d2ccfdc0895c67ed7
# Removable storage filesystem support.
TW_INCLUDE_NTFS_3G := true

228
CMD_0X18_INVESTIGATION.md Normal file
View file

@ -0,0 +1,228 @@
# Sycamore Android 16 FBE cmd 0x18 investigation
Date: 2026-08-27
Baseline: hardware-tested V14 wrapped-first vendor_boot, SHA-256
`d657365e5c14ba0bc1e93e9fc0ec38acb18f935e222d1bae66377011b96b9d15`.
Evidence labels in this report are **PROVEN**, **EXPECTED**, **UNKNOWN**, and
**EXCLUDED**.
## A. Recovery state immediately before the first cmd 0x18
The first V14 conversion is at `v14-native-runtime-failure/logcat-all.txt:2942-2957`,
at 19:01:29.957-19:01:29.959. Before that point recovery had already proven:
- `vendor.soter.teei.init=INIT_OK`
- `vendor.soter.teei.km.init=config_patch_level_sucess`
- `vendor.soter.teei.km.version=v2.0.0`
- `vendor.soter.teei.persist=OPEN`
- `vendor.soter.teei.rpmb.flag=ok`
- persist selector 3
- successful c09 TA load, secure-storage manager startup, physical RPMB commands,
`rpmb key is programmed`, KeyMint configuration, SharedSecret, metadata unwrap,
systemwide outer unwrap, and one stock-shaped Begin/Update/Finish sequence.
The retained recovery property dump does not contain:
- `vendor.soter.teei.googlekey.status`
- `vendor.soter.teei.logini`
Recovery later logs `logini thread failed` at 19:02:05.108, about 35 seconds after
the first failure. This proves a final recovery-state difference, but does not prove
that logini participates in cmd 0x18.
## B. Healthy-stock Soter comparison
The archived stock snapshot contains:
| Property | Stock snapshot | V14 retained state before/around failure |
|---|---|---|
| `vendor.soter.teei.googlekey.status` | `ok` | absent |
| `vendor.soter.teei.init` | `INIT_OK` | `INIT_OK` |
| `vendor.soter.teei.km.init` | `config_patch_level_sucess` | same |
| `vendor.soter.teei.km.version` | `v2.0.0` | `v2.0.0` |
| `vendor.soter.teei.logini` | `start` | absent; later logini thread failure |
| `vendor.soter.teei.persist` | `OPEN` | `OPEN` |
| `vendor.soter.teei.rpmb.flag` | `ok` | `ok` |
Stock `microtrust.rc:245-246` sets `vendor.soter.teei.logini=start` on the Android
`post-fs-data` trigger. It is an init-set request to `teei_daemon`, not proof by
itself that a secure-world transition completed. The stock property snapshot was
captured after DE storage was available, so it cannot prove that logini preceded
stock's first storage-key conversion. Android init describes `post-fs-data` as the
stage at which `/data` has already been mounted; therefore forcing logini before
conversion would not reproduce a proven stock ordering.
No local rc file was found that directly sets `googlekey.status`. The value is
therefore **EXPECTED** to be service/secure-world-derived, but its exact setter and
pre-conversion timing remain **UNKNOWN**.
Conclusion: `persist=OPEN` and `rpmb.flag=ok` are matched and correspond to observed
real VFS/RPMB activity. `logini` and `googlekey.status` differ in final visibility,
but neither is yet connected to cmd 0x18 timing.
## C. Exact 64-byte storage-key provenance
The source directory is `/data/unencrypted/key`:
1. `keymaster_key_blob` is a 444-byte ordinary KeyMint AES wrapping-key blob,
SHA-256
`24449b28dedeecc82b0abfb571ef6aa93151608e841fbbf9dd9bdb907ff89c87`.
2. `secdiscardable` contributes to the application ID computed by
`KeyStorage.cpp::generateAppId()`.
3. `encrypted_key` is a 92-byte AES-GCM container: 12-byte nonce, 64-byte
ciphertext/plaintext length, and 16-byte tag. Its SHA-256 is
`7700695d0e5c1605d9d1c56669cb4130588c1054766ac735488fa76f4fd47312`.
4. `retrieveKey()` reads the files at `system/vold/KeyStorage.cpp:630-652`.
5. `decryptWithKeymasterKey()` authenticates and decrypts the 92-byte container.
6. The resulting 64-byte plaintext is the proprietary `TAG_STORAGE_KEY` blob.
7. `exportWrappedStorageKey()` at `system/vold/KeyStorage.cpp:189-197` copies it
without transformation into `Keymaster::exportKey()`.
8. `Keymaster::exportKey()` at `system/vold/Keymaster.cpp:167-191` places the same
bytes in a `Domain::BLOB` descriptor.
9. Keystore2 at `system/security/keystore2/src/security_level.rs:892-962` passes
the byte vector unchanged to `IKeyMintDevice::convertStorageKeyToEphemeral()`.
10. Beanpod serializes the legacy export request as 12-byte empty AuthorizationSet,
4-byte RAW KeyFormat, 4-byte blob length, and the 64-byte blob: 84 bytes total.
V14 hash-only checkpoints A, B, C, and D all report 64 bytes with SHA-256
`3c36a06c93c07adeda069dd413b2df8f2e9b54371f3920e5de40ea6f318e8f83`.
The complete 84-byte request SHA-256 is
`7c4f5fdd21de15b396f5b739cbb41f32a3944046902c786ceed9a2b829e56647`.
No raw key, nonce, HMAC, or credential bytes are recorded. This is intentional:
lengths and hashes prove transport identity without exposing live key material.
The 64-byte length is **PROVEN expected for this exact existing Sycamore blob**:
stock decrypts the same key lineage, the authenticated outer container has a
64-byte plaintext, and the TA reports `DeserializeAuthEncryptedBlob ... (64)`.
## D. Recovery code path and cmd 0x18 construction
`fscrypt_initialize_systemwide_keys()` at `system/vold/FsCrypt.cpp:452-495`
retrieves `/data/unencrypted/key`, then `install_storage_key()` at lines 268-279
calls `exportWrappedStorageKey()` when hardware-wrapped mode is selected.
The request contains:
- empty client/application AuthorizationSets;
- `KeyFormat::RAW`;
- the unmodified 64-byte opaque blob;
- no credential, SID, auth token, application ID, or application data.
Beanpod command `0x18` is its legacy Keymaster export-key command encoding. It is
not a distinct 0x18-only userspace serializer invented by TWRP.
## E. Android 16 comparison
Android 16 AOSP `system/vold` retains the same architecture:
- vold passes the opaque storage key as `Domain::BLOB` to Keystore2;
- Keystore2 calls `convertStorageKeyToEphemeral()` directly;
- only `KEY_REQUIRES_UPGRADE` invokes `upgradeKey()` and retries;
- vold intentionally continues storing its original storage blob because its outer
wrapper already handles version binding.
V14 follows this behavior. Beanpod returns `INVALID_KEY_BLOB`, not
`KEY_REQUIRES_UPGRADE`, so the absent upgrade retry is correct.
Material source drift found in the old tree is creation-side, not conversion-side:
- the local `generateWrappedStorageKey()` requests rollback resistance through its
generic generator and adds private `KM_TAG_FBE_ICE`;
- newer AOSP avoids rollback resistance for newly generated storage keys.
Neither changes the already-existing 64-byte stock blob or its conversion request.
The exact stock A16 vold binary contains `wrappedkey_v0`,
`fscrypt_initialize_systemwide_keys`, and the same ephemeral-key path. A
syntactically or semantically obsolete recovery request is therefore **EXCLUDED at
the presently visible boundary**.
## F. Public recovery references
- Agate: credible working TWRP + Microtrust/Beanpod + FBE/metadata reference, but
userdata lacks `wrappedkey_v0`; it does not exercise cmd 0x18.
- Pissarro: useful Beanpod/TEEI startup ordering, but no proven hardware-wrapped
storage-key decryption.
- Air: closest MT6835 architecture reference, but crypto flags are disabled and the
tree is explicitly WIP.
- Dew: modern AIDL KeyMint/Keystore2 architecture, but WIP and no verified
hardware-wrapped-key decryption result.
- TB351FU: valuable modern Lenovo Soter property control; its published recovery
reports encrypted internal storage inaccessible and its stock fstab does not
establish hardware-wrapped-key use.
No verified public “unicorn” was found that simultaneously demonstrates MediaTek,
Microtrust/TEEI, Beanpod, `wrappedkey_v0`, and successful recovery decryption.
## G. Microtrust hidden state
The MT6895 public driver names `boot_decryto_lock`, `boot_soter_flag`,
`keymaster_call_flag`, `soter_error_flag`, and `teei_capi_ready`, and enforces
`teei_daemon` identity for VFS calls. These establish that hidden kernel/secure-world
state exists. Sycamore runtime proves VFS, TA load, RPMB, and ordinary KeyMint calls
advance far enough to work.
No local Sycamore kernel source exposes equivalent variables, and no retained trace
connects any one of them to cmd 0x18. They remain architectural context, not a
root-cause result.
The exact Sycamore TA is a 930,857-byte signed/encrypted Microtrust container, not
an ELF image, so normal `readelf`/`objdump` analysis cannot isolate its dispatcher.
Runtime secure-world diagnostics nevertheless identify the decisive path:
`ParseKeyBlob()` -> `DeserializeAuthEncryptedBlob()` -> `AES_decrypt_ctr()` ->
integrity/hash comparison failure -> `-33`.
This is after the 64-byte blob has reached authenticated blob decoding. It strongly
favors a structurally recognized blob whose authentication/derivation context does
not reproduce stock over a malformed 84-byte request.
## H. Ranked remaining causes
1. **Medium-high:** secure-world storage-key authentication/KDF context differs
between normal boot and recovery. This best matches the TA's integrity comparison
failure while ordinary blobs and the same session work.
2. **Medium:** a storage-specific secure-world initialization transition occurs in
stock but not recovery before conversion. `googlekey.status`/logini are observable
leads, but their timing and causal relationship are unproven.
3. **Medium-low:** the retained 64-byte blob depends on secure-storage lineage/state
not exercised by ordinary metadata keys. RPMB transport works, but that does not
prove every SST namespace/derived key is identical.
4. **Low:** userspace serialization or Android-version drift. A16 and recovery paths
match, and A/B/C/D plus the complete request hash exclude mutation.
5. **Low:** malformed length/version presented by vold. The outer container
authenticates, 64 bytes is the actual stored object, and TA reaches integrity
comparison rather than rejecting request structure.
Root-of-Trust/device-lock state remains a possible input to item 1, not a proven
cause. Current-stock success under the same raw unlocked/orange boot properties
prevents asserting a simple property-level ROT mismatch.
## I. Best next controlled experiment
The smallest justified next artifact is diagnostic-only: snapshot the seven Soter
properties inside `Keymaster::exportKey()` immediately before the first
`convertStorageKeyToEphemeral()` call. This proves exact boundary-time recovery
state without setting properties, changing service order, exposing secrets, or
altering the request.
Do **not** set `logini=start` yet. Stock sets it on `post-fs-data`, and available
evidence does not prove that doing so before cmd 0x18 is stock-equivalent or safe.
The stock-side unresolved requirement is a trustworthy early-boot timing capture.
The existing post-DE stock property dump cannot answer it, and invasive stock
instrumentation could invalidate the control.
## J. Exact proposed change
One diagnostic addition in `system/vold/Keymaster.cpp`, immediately before
`securityLevel->convertStorageKeyToEphemeral(...)`:
- read and log only the seven public `vendor.soter.teei.*` property strings;
- keep the existing 64-byte length/SHA-256 checkpoint;
- do not set any property or alter control flow.
No TA, Beanpod, Keystore2, KeyStorage, init, fstab, DTB, PLATFORM fragment, or key
file would change functionally.

92
README.md Normal file
View file

@ -0,0 +1,92 @@
Device configuration for Motorola Moto Pad 2026 (codenamed "sycamore_row_5G")
=========================================
The Motorola Moto Pad 2026 / XT2575-4 (codenamed _"sycamore_row_5G"_) is an Android tablet from Motorola Mobility based on the MediaTek MT8755 platform.
This device tree is intended for building Team Win Recovery Project (TWRP).
## Device specifications
Basic | Spec Sheet
-------:|:-------------------------
Model | Motorola XT2575-4
Codename | sycamore_row_5G
SoC | MediaTek MT8755 (MT6835 platform)
CPU | 64-bit ARM, Cortex-A76 / Cortex-A55
GPU | ARM Mali
Shipped Android Version | Android 15
Tested Stock Firmware | Android 16 / ZUI 17.5
Storage | UFS
Display | 2560 x 1600 pixels, 90 Hz
Partition Scheme | A/B, Virtual A/B, Dynamic Partitions
Encryption | File-Based Encryption (FBE v2) with metadata encryption
Filesystem | F2FS userdata, EROFS/EXT4 dynamic partitions
Recovery Layout | TWRP recovery ramdisk stored in `vendor_boot`
Vendor Boot Header | Version 4
## Device picture
![Motorola Moto Pad (2026)](https://fdn2.gsmarena.com/vv/bigpic/motorola-moto-pad-2026-.jpg)
## Device reference
[Motorola Moto Pad (2026) - GSMArena](https://www.gsmarena.com/motorola_moto_pad_(2026)-14582.php)
# Status
Current state of features:
- [x] Correct screen/recovery size
- [x] Working display
- [x] Working touch
- [x] Screen brightness control
- [x] Power off
- [x] Reboot to system
- [x] Reboot to bootloader
- [x] Reboot to recovery
- [x] Fastboot / fastbootd
- [x] ADB
- [x] Internal storage detected
- [x] External SD card detected
- [x] exFAT external storage
- [x] F2FS userdata support
- [x] EXT4 support
- [x] EROFS support
- [x] Dynamic partition support
- [x] A/B slot handling
- [x] `vendor_boot` v4 support
- [x] Stock PLATFORM vendor ramdisk preserved
- [x] TWRP RECOVERY vendor ramdisk fragment
- [x] Metadata encryption successfully decrypted
- [x] `/data` block device successfully decrypted and mounted
- [x] KeyMint service
- [x] Gatekeeper service
- [x] Keystore2 service
- [ ] Full FBE decryption
- [ ] User PIN/password decryption
- [ ] Decrypted `/data/media`
- [ ] MTP
- [ ] Vibration / haptics
- [ ] USB mass storage export
- [ ] TWRP native `vendor_boot` installation from pristine stock image
- [ ] All commonly modified partitions exposed for image flashing
## Encryption status
Metadata encryption is working and TWRP successfully creates and mounts the decrypted userdata block device.
Current testing reaches the Android hardware-wrapped storage key path but fails during system-wide fscrypt key initialization.
The current failure occurs in the MediaTek Beanpod KeyMint trusted application while processing storage-key conversion:
```text
cmd 0x18 / cmd 24
km_error = -33
AES_decrypt_ctr:
decrypt keyblob compare hash failed
DeserializeAuthEncryptedBlob:
decrypt keyblob failed (-33)
**Copyright (C) 2023 A-Team Digital Solutions**

View file

@ -0,0 +1,85 @@
# V15 Soter pre-0x18 state snapshot
Date: 2026-08-27
Result: **PASS — diagnostic-only one-variable build; not flashed.**
Artifact SHA-256:
`d2f542f8d475bc3699487647a041e16235f20847ee43d23e5545b55e6f2b8b1c`
Artifact size: 67,108,864 bytes.
## Hypothesis and change scope
The retained V14 property dump did not prove the exact values of seven public
Microtrust/Soter properties at the instant immediately before the first cmd 0x18.
The only functional source delta from V14 is in
`system/vold/Keymaster.cpp::Keymaster::exportKey()`: immediately after the existing
length/SHA-256 checkpoint B and before
`securityLevel->convertStorageKeyToEphemeral()`, recovery reads and logs:
- `vendor.soter.teei.googlekey.status`
- `vendor.soter.teei.init`
- `vendor.soter.teei.km.init`
- `vendor.soter.teei.km.version`
- `vendor.soter.teei.logini`
- `vendor.soter.teei.persist`
- `vendor.soter.teei.rpmb.flag`
The marker is `SYCA_SOTER_PRE_0X18`. The patch sets no property, changes no
service ordering, changes no request bytes, and exposes no key material.
## Final-image differential
Against the preserved pre-V15 current recovery fragment, complete unpacked-file
SHA-256 manifests differ only at:
- `system/bin/recovery`
- before: `439d1fa5a5080e3ff5f293d18deb1e34aa21b48dc9ba9fd01e7e982fa83840e3`
- V15: `53683ad4e34ffb6ebe4ffb705037f224f62334d2e355b5359230cc60e106077a`
- `ramdisk-files.sha256sum`
- `ramdisk-files.txt`
The final recovery binary contains `SYCA_SOTER_PRE_0X18`, the requested property
names, and the existing `SYCA_DEKEY_A/B` diagnostics.
## Frozen final-image hashes
- PLATFORM:
`06738f638a745075511c571347c5511331dda16b84d1ee88cda160c2dc7ebb14`
- DTB:
`0e93b71aacf707a5fbb7a58eff73995b92d766a31a98d8d70e91f548c9e1d5af`
- Beanpod:
`e066ff6e4f0aaa803e6633aa8fad1f54335e5b969f6422fedc44f77b033851e9`
- `libTEECommon.so`:
`d700e663bd3f68611cadef9bfc929aeb819e199cc9ef3f61a9c8d0afc2bed5b0`
- KeyMint TA:
`af4d1dc971c80b539de9e73ee792285944dd9b132d267be3bb0496bd82bb75ab`
- `teei_daemon`:
`fb79a0c59dab04edbf5ff4cd10af601c4d3dba5378b5ba3d981553f818fbb014`
- `libteei_daemon_vfs.so`:
`6012c5ccb298a411c8f7c0572b26ca237c3209ad348cd6e509a2cf71f4fccc13`
- Keystore2:
`31a9f7d9de497e06c465343ddb4d3aa3e8064c3051059c206b53d061d59f6256`
- `libkeystore2_crypto.so`:
`6e46dbfbf135c41131d64b91ce93fedf7c62077059fdbc24a5ca62b94e195132`
## vendor_boot audit
- header v4; page size 4096
- cmdline: `bootopt=64S3,32N2,64N2`
- fragment 0: exact stock PLATFORM, 27,422,144 bytes
- fragment 1: RECOVERY named `recovery`, 31,678,798 bytes
- total vendor ramdisk: 59,100,942 bytes
- stock DTB exact
- bootconfig empty
- PLATFORM first, RECOVERY second
- all board_id words zero
- recovery fragment `.ko` count: zero
- only five expected module metadata files retained
- algorithm-NONE AVB footer and vendor_boot hash verify
- post-AVB image re-unpacks successfully
- final size exactly 67,108,864 bytes
Static audit: **PASS. Stop before flashing.**

View file

@ -0,0 +1,115 @@
# V16 boot-decrypt-unlock static audit
Date: 2026-08-27
Result: **PASS — one-variable boot-decrypt lifecycle experiment; not flashed.**
Artifact SHA-256:
`1fe86baa602403638e2f0c0b2b7a29bc4cd768ec2bed9256b12aa92c8ca81e09`
Artifact size: 67,108,864 bytes.
## Hypothesis
The exact stock `teei_daemon` snapshots `ro.crypto.state` and
`ro.crypto.type` in its boot-decrypt thread. With `ro.crypto.type=file`, it
logs `fbe mode late` and issues the `/dev/teei_config` boot-decrypt-unlock
ioctl. V15 did not expose that initial FBE state and never set
`vendor.soter.teei.logini=start`; consequently it showed neither the daemon
unlock message nor a boot-decrypt-lock result before cmd 0x18.
V16 tests whether reproducing those stock lifecycle inputs before the first
storage-key conversion changes cmd 0x18 from `KM_ERROR_INVALID_KEY_BLOB`.
## Exact source changes
- `device/motorola/sycamore_row_5G/device.mk:16-22`
- adds `ro.crypto.state=encrypted` and `ro.crypto.type=file` through
`PRODUCT_SYSTEM_DEFAULT_PROPERTIES`, placing both in the initial recovery
`prop.default` before `teei_daemon` starts.
- `device/motorola/sycamore_row_5G/recovery/root/init.recovery.crypto.rc:69-73`
- sets `vendor.soter.teei.logini=start` once the existing Soter init,
persist-VFS, and RPMB health properties simultaneously report their stock
terminal values.
- `system/vold/Keymaster.cpp:187-213`
- adds the secret-free `SYCA_V16_PRE_0X18` state snapshot immediately before
`convertStorageKeyToEphemeral()`; V15 tracing remains present.
No other functional source was changed for V16. Cmd 0x18 serialization,
wrapped-key bytes, Beanpod, TA, private libraries, SharedSecret, patch levels,
DTB, PLATFORM, and kernel modules are unchanged.
## Implemented ordering
1. Initial property load exposes the real recovery FBE mode:
`ro.crypto.state=encrypted`, `ro.crypto.type=file`.
2. `on init` prepares the existing TEE device nodes and starts the exact stock
`teei_daemon`.
3. `init.svc.teei_daemon=running` starts Beanpod and Gatekeeper as in V15.
4. Once `vendor.soter.teei.init=INIT_OK`,
`vendor.soter.teei.persist=OPEN`, and
`vendor.soter.teei.rpmb.flag=ok` are all true, init sets
`vendor.soter.teei.logini=start`.
5. Existing vold flow reaches the first storage-key conversion, where V16 logs
the complete pre-0x18 public-state snapshot.
No sleep or repeated trigger was added. `vold.post_fs_data_done` and
`vendor.soter.teei.crypto.state` were not set: the exact daemon does not
reference the former, and its `ro.crypto.type=file` startup branch does not
require the latter.
## Final recovery-fragment differential against archived V15
Complete relative-path SHA-256 manifests differ only at:
- `init.recovery.crypto.rc` (intentional logini trigger)
- `prop.default` (intentional two FBE defaults)
- `system/bin/recovery` (intentional V16 marker)
- `ramdisk-files.sha256sum` and `ramdisk-files.txt` (generated inventory)
There are no added or removed payload paths.
## Frozen binary/content verification
- stock PLATFORM:
`06738f638a745075511c571347c5511331dda16b84d1ee88cda160c2dc7ebb14`
- stock DTB:
`0e93b71aacf707a5fbb7a58eff73995b92d766a31a98d8d70e91f548c9e1d5af`
- exact stock `teei_daemon`:
`fb79a0c59dab04edbf5ff4cd10af601c4d3dba5378b5ba3d981553f818fbb014`
- Beanpod:
`e066ff6e4f0aaa803e6633aa8fad1f54335e5b969f6422fedc44f77b033851e9`
- KeyMint TA:
`af4d1dc971c80b539de9e73ee792285944dd9b132d267be3bb0496bd82bb75ab`
- Keystore2:
`31a9f7d9de497e06c465343ddb4d3aa3e8064c3051059c206b53d061d59f6256`
- `libkeystore2_crypto.so`:
`6e46dbfbf135c41131d64b91ce93fedf7c62077059fdbc24a5ca62b94e195132`
All nine stock KeyMint private-library hashes and the V15 keytrace library hash
match the frozen V15 family. The recovery fragment contains zero `.ko` files
and only the same five module metadata files.
## Final vendor_boot audit
- header version 4; page size 4096
- command line `bootopt=64S3,32N2,64N2`
- bootconfig empty
- fragment 0: unnamed PLATFORM, type 1, 27,422,144 bytes, exact stock hash
- fragment 1: `recovery`, RECOVERY type 2, 31,679,640 bytes
- total vendor ramdisk size: 59,101,784 bytes
- all board IDs zero; PLATFORM first and RECOVERY second
- exact stock DTB, 192,119 bytes
- algorithm-NONE AVB footer verifies
- final image re-unpacks successfully
- final partition-padded size exactly 67,108,864 bytes
## Expected hardware-test markers
- `SYCA_V16_PRE_0X18`
- `daemon unlock keymaster signal to tz-driver`
- `keymaster unlock boot_decrypt_lock success` or `failed!`
- existing `SYCA_SOTER_PRE_0X18`, `SYCA_DEKEY_A/B/C/D`, `SYCA_TA_TRACE`,
cmd 0x18 hashes, and cmd result
Static audit: **PASS. Stop before flashing.**

View file

@ -0,0 +1,148 @@
# V17 stock THH identity static audit
Date: 2026-08-27
Result: **PASS — controlled stock-THH/product-identity experiment; not flashed.**
Artifact SHA-256:
`f23330a09d0b0ea4a7c902342dd62f36dcf5179cd72ed40d316819deb7c9ec50`
Artifact size: 67,108,864 bytes.
## Hypothesis
V16 initialized Microtrust with an incomplete `/vendor/thh/ta` namespace and
the recovery codename as `ro.product.device`. The exact signed stock model
configuration recognizes `platform=mt8755`, `model=XT2575-4`, while V16 secure
world logged `model:sycamore_row_5G`, model verification failures, and TA load
error 18. V17 tests whether presenting the complete exact-stock THH payload
and production Microtrust-facing identity before the first `teei_daemon`
launch restores cold TA/model validation and changes cmd 0x18 behavior.
All V16 FBE/logini behavior and crypto diagnostics are retained unchanged.
## Exact stock source
The files were copied from the preserved exact-firmware vendor extraction:
`/home/nicholas/Downloads/sycamore-twrp-info/first-hardware-boot/fbe-a16-v3/ta-forensics/stock-vendor-thh/ta`
This extraction belongs to factory baseline
`XT2575-4_ROW_OPEN_USER_M1317.3_W_ZUI_17.5.10.017_ST`. Both the staged source
tree and the final unpacked recovery fragment were compared byte-for-byte with
all 15 source files. Both comparisons pass.
## Stock THH manifest
| File | Bytes | SHA-256 |
|---|---:|---|
| `0102030405060708090a0b0c0d0e0f10.ta` | 20,777 | `19a5e15a2f17a6c1418c5edc6fc63a3ddd279966540930b2e97f227ce0d897ae` |
| `020f0000000000000000000000000000.ta` | 22,649 | `aba910dfbdccbf177eb107853669c6eff689b4f36e9afb2f8e04e30d8e97108b` |
| `06090000000000000000000000000000.ta` | 6,953 | `2d5cd2e906a1dab1636c59bbe037f8502aec7c25edc9172cba8da158fc6d8c36` |
| `08030000000000000000000000000000.ta` | 24,393 | `ba29286b7d24edc04275b195db120032e116828ff73241241d6d8c40855b4f03` |
| `08110000000000000000000000000000.ta` | 72,073 | `0fb3e0b70a972d4a58088978aec706399009f32f5133061efe6b7033f1ae9f17` |
| `40188311faf343488db888ad39496f9a.ta` | 9,177 | `5cb79b594721523700fe4708ea764c80144979dc698db27a18a2383159028a1c` |
| `5020170115e016302017012521300000.ta` | 10,393 | `0865a891c965a201d4e64f3b9e2f4a3f0fe899f80ff24ab88cf75b1622dc5d40` |
| `5f7a5b3b29b041bca249524a031a00e3.ta` | 32,737 | `2911bd3726ae4110877409d501329f36f5958a6686262b93814d8bb89fdb629c` |
| `8888c04fc30c4dd0a319ea29643d4d4c.ta` | 32,129 | `5b812b524f5eea79c1bc944b06d00cddc8738274166d888bf255080ab0feab09` |
| `abcd270ea5c44c58bcd3384a2fa2539e.ta` | 39,833 | `c4d5811efd695ba0550960f9c5649e6476289cbb00cb8c9d7f5cb11b7a2e2f18` |
| `c09c9c5daa504b78b0e46eda61556c3a.ta` | 930,857 | `af4d1dc971c80b539de9e73ee792285944dd9b132d267be3bb0496bd82bb75ab` |
| `c1882f2d885e4e13a8c8e2622461b2fa.ta` | 55,537 | `0a5f34be1d73535c563aaece231b2e93193ae5f2c69b8050e71b14f48956081d` |
| `d91f322ad5a441d5955110eda3272fc0.ta` | 22,177 | `4432d3f85370e41f57bf5fb6b5945326c441a394d0cbe298be5c29b2b056b954` |
| `e97c270ea5c44c58bcd3384a2fa2539e.ta` | 447,769 | `c76352a8115aba85e963d2e39466530daa8ac5db411ccea403eaf84d2c308e96` |
| `isee_model.json` | 2,794 | `67c019209942f9a68408d998e5d478b0dba5e3f5dbbc6582b146af8aaa05e3c6` |
## Source/change scope
- `device/motorola/sycamore_row_5G/device.mk`
- packages all 15 stock THH files and the early diagnostic script;
- adds only global `ro.product.device=XT2575-4` to the existing V16 initial
system-default property set.
- `build/make/core/Makefile`
- recovery-only, target-gated, fail-closed replacement of the two generated
vendor device/name values in final `prop.default`; vendor model was already
stock-correct.
- `device/motorola/sycamore_row_5G/recovery/root/init.recovery.crypto.rc`
- synchronously executes the readiness diagnostic immediately before the
existing first `start teei_daemon`.
- `device/motorola/sycamore_row_5G/recovery/root/system/bin/syca_v17_thh_ready.sh`
- logs only public properties, four existence results, and total file count.
- fourteen stock THH files were added under
`device/motorola/sycamore_row_5G/recovery/root/vendor/thh/ta`; the fifteenth,
the KeyMint TA, was already present and byte-identical.
## Final initial properties
- `ro.product.device=XT2575-4`
- `ro.product.vendor.device=XT2575-4`
- `ro.product.vendor.name=XT2575-4`
- `ro.product.vendor.model=XT2575-4`
- `ro.build.product=sycamore_row_5G` (preserved)
- `ro.crypto.type=file` (V16 preserved)
- `ro.crypto.state=encrypted` (V16 preserved)
`PRODUCT_DEVICE=sycamore_row_5G`, the TWRP product name, target assertions, and
`ro.twrp.target.devices` behavior were not changed.
## Cold-boot ordering
1. Recovery ramdisk, all 15 THH files, production identity, and V16 FBE
properties exist before init actions.
2. Existing TEE/RPMB nodes and read-only persist mount are prepared.
3. Init synchronously runs `syca_v17_thh_ready.sh` and emits
`SYCA_V17_THH_READY`; expected `thh_file_count=15`.
4. Init launches the unchanged exact-stock `teei_daemon` for the first time.
5. The daemon performs cold secure-world TA/model validation.
6. Existing `init.svc.teei_daemon=running` action starts Beanpod/Gatekeeper.
7. Existing V16 healthy-state conjunction sets `logini=start`.
8. Existing vold/Keystore2 path reaches the first cmd 0x18.
No sleep, restart, bind mount, or post-initialization substitution was added.
## V16-to-V17 final-image differential
Complete unpacked relative-path SHA-256 manifests show only:
- modified: `init.recovery.crypto.rc`, `prop.default`, and generated ramdisk
inventory files;
- added: readiness script and the 14 THH files absent from V16;
- unchanged: `system/bin/recovery`, existing KeyMint TA, all secure binaries,
private libraries, Keytrace, and every other ramdisk payload.
## Frozen architecture/binary verification
- stock PLATFORM:
`06738f638a745075511c571347c5511331dda16b84d1ee88cda160c2dc7ebb14`
- stock DTB:
`0e93b71aacf707a5fbb7a58eff73995b92d766a31a98d8d70e91f548c9e1d5af`
- exact stock `teei_daemon`:
`fb79a0c59dab04edbf5ff4cd10af601c4d3dba5378b5ba3d981553f818fbb014`
- Beanpod:
`e066ff6e4f0aaa803e6633aa8fad1f54335e5b969f6422fedc44f77b033851e9`
- KeyMint TA:
`af4d1dc971c80b539de9e73ee792285944dd9b132d267be3bb0496bd82bb75ab`
- Keystore2:
`31a9f7d9de497e06c465343ddb4d3aa3e8064c3051059c206b53d061d59f6256`
- `libkeystore2_crypto.so`:
`6e46dbfbf135c41131d64b91ce93fedf7c62077059fdbc24a5ca62b94e195132`
- Keytrace:
`0642e36b25589ebdf16423cb3a81c8ba5dab3ffc0dcb32a58386efa8b2d7293d`
The final recovery has zero `.ko` files and the same five module metadata
files. V16 `SYCA_V16_PRE_0X18`, `SYCA_SOTER_PRE_0X18`,
`SYCA_DEKEY_A/B/C/D`, and `SYCA_TA_TRACE` markers remain present.
## Final vendor_boot audit
- header v4; page size 4096
- cmdline `bootopt=64S3,32N2,64N2`; bootconfig empty
- fragment 0: unnamed PLATFORM/type 1, 27,422,144 bytes, exact stock hash
- fragment 1: `recovery`/type 2, 32,078,301 bytes
- total vendor ramdisk: 59,500,445 bytes
- stock DTB exact; all board IDs zero
- PLATFORM first, RECOVERY second
- algorithm-NONE AVB footer and vendor_boot hash verify
- final image re-unpacks successfully
- final partition-padded size exactly 67,108,864 bytes
Static audit: **PASS. Stop before flashing.**

View file

@ -0,0 +1,158 @@
# V18 stock tee identity static audit
Date: 2026-08-27
Result: **PASS — controlled teei_daemon execution-identity experiment; not flashed.**
Artifact SHA-256:
`7c6817d5b29055ac337b26a87e3c29281e8ff2df2209521546c75bb8064642ac`
Artifact size: 67,108,864 bytes.
## Hypothesis
V17 fixed cold THH/model validation but still ran the unchanged stock
`teei_daemon` as root in `u:r:recovery:s0`. V18 tests whether the missing
boot-decrypt-unlock transition and cmd 0x18 key-blob failure depend on the
daemon's production process identity: UID/GID 1000, no supplementary groups,
only `CAP_SYS_RAWIO`, and enforcing `u:r:tee:s0`.
No KeyMint, wrapped-key, FBE, secure-storage, vendor_boot, kernel, or binary
behavior was otherwise changed.
## Service declaration differential
V17:
```rc
user root
group root system
capabilities SYS_RAWIO
seclabel u:r:recovery:s0
```
V18:
```rc
user system
group system
capabilities SYS_RAWIO
seclabel u:r:tee:s0
```
The complete 21-entry `-r`/`-t` UUID argument sequence is byte-for-byte
unchanged. A single `group system` entry establishes the primary GID only; no
supplementary group is declared. `SYS_RAWIO` is the sole capability token.
## SELinux audit and implementation
The pre-V18 platform policy already defined:
- `tee` with the `domain` attribute;
- `tee_exec` as an executable vendor-file type;
- `init_daemon_domain(tee)`, producing
`type_transition init tee_exec:process tee`.
V17 lacked a `tee_exec` path label for `/vendor/bin/teei_daemon`, so merely
changing `seclabel` could not prove a valid launch environment.
V18 adds the exact executable context and the narrowly scoped stock Microtrust
device contexts under `device/motorola/sycamore_row_5G/sepolicy/vendor`. The
final combined policy:
- compiles successfully;
- passes neverallow checks;
- contains `tee` in the `domain` attribute;
- contains the init→`tee` transition;
- permits `tee` to use only the declared Microtrust node types;
- permits `tee` `sys_rawio` capability use;
- does **not** mark `tee` permissive.
Final policy SHA-256:
`0b23e52b72848a03649e78fe005b88827d35a37bcab2ce8d394fc6d395fd94d3`
## Exact node contexts
These are copied from the exact stock vendor_boot `vendor_file_contexts`:
| Path | Final type |
|---|---|
| `/dev/isee_tee0` | `teei_client_device` |
| `/dev/teei_client` | `teei_client_device` |
| `/dev/teei_config` | `teei_config_device` |
| `/dev/tz_vfs` | `teei_vfs_device` |
| `/dev/teei_fp` | `teei_fp_device` |
| `/dev/ut_keymaster` | `ut_keymaster_device` |
| `/dev/0:0:0:49476` | `teei_rpmb_device` |
| `/dev/rpmb0` | `teei_rpmb_device` |
| `/dev/utr_tui` | `utr_tui_device` |
| `/vendor/bin/teei_daemon` | `tee_exec` |
No non-Microtrust device path was relabeled. These labels are required for the
enforcing `tee` domain to use the same interfaces that V17 accessed from the
permissive recovery domain.
## Removed failed V17 diagnostic
The packaged `syca_v17_thh_ready.sh` and its synchronous init `exec` were
removed. This eliminates the known invalid-domain exec failure and does not add
a replacement delay or process. V16/V17 crypto diagnostics remain intact.
## Frozen V17/V16 state
- all 15 stock THH files: byte-identical to the exact stock extraction;
- `isee_model.json`: unchanged;
- `ro.product.device=XT2575-4`;
- all three vendor product identity fields: `XT2575-4`;
- `ro.build.product=sycamore_row_5G`;
- `ro.crypto.type=file` and `ro.crypto.state=encrypted`;
- healthy-state `vendor.soter.teei.logini=start` ordering;
- `SYCA_V16_PRE_0X18`, `SYCA_SOTER_PRE_0X18`,
`SYCA_DEKEY_A/B/C/D`, and `SYCA_TA_TRACE`.
## Frozen hashes
- stock PLATFORM:
`06738f638a745075511c571347c5511331dda16b84d1ee88cda160c2dc7ebb14`
- stock DTB:
`0e93b71aacf707a5fbb7a58eff73995b92d766a31a98d8d70e91f548c9e1d5af`
- exact stock `teei_daemon`:
`fb79a0c59dab04edbf5ff4cd10af601c4d3dba5378b5ba3d981553f818fbb014`
- Beanpod:
`e066ff6e4f0aaa803e6633aa8fad1f54335e5b969f6422fedc44f77b033851e9`
- KeyMint TA:
`af4d1dc971c80b539de9e73ee792285944dd9b132d267be3bb0496bd82bb75ab`
- Keystore2:
`31a9f7d9de497e06c465343ddb4d3aa3e8064c3051059c206b53d061d59f6256`
- Keytrace:
`0642e36b25589ebdf16423cb3a81c8ba5dab3ffc0dcb32a58386efa8b2d7293d`
## V17-to-V18 final-image differential
Complete unpacked relative-path SHA-256 manifests differ only at:
- `init.recovery.crypto.rc` — intended service identity and failed-diagnostic
removal;
- `sepolicy`, `vendor_file_contexts`, and `file_contexts.bin` — intended narrow
tee-domain implementation;
- removal of `system/bin/syca_v17_thh_ready.sh`;
- generated ramdisk inventory files.
No secure binary, THH payload, property file, kernel module, or crypto
diagnostic binary changed.
## Final vendor_boot audit
- header v4; page size 4096
- cmdline `bootopt=64S3,32N2,64N2`; bootconfig empty
- fragment 0: unnamed PLATFORM/type 1, 27,422,144 bytes, exact stock hash
- fragment 1: `recovery`/type 2, 32,078,200 bytes
- total vendor ramdisk: 59,500,344 bytes
- exact stock DTB; all board IDs zero
- PLATFORM first, RECOVERY second
- recovery `.ko` count: zero; same five module metadata files
- algorithm-NONE AVB footer and vendor_boot hash verify
- final image re-unpacks successfully
- final partition-padded size exactly 67,108,864 bytes
Static audit: **PASS. Stop before flashing.**

View file

@ -0,0 +1,112 @@
# Sycamore stock-vs-TWRP vendor_boot fragment audit
Date: 2026-08-27
## Inputs
- Factory: `/home/nicholas/Downloads/sycamore-twrp-info/vendor_boot_a.img`
- SHA-256: `e542af82e8bb153faa96a44ec480548b21ac99abf7ca26ffca0eafcc9b6a9fe`
- Hardware-tested V14: `/home/nicholas/Downloads/sycamore-twrp-info/first-hardware-boot/fbe-a16-v14-wrapped-first/vendor_boot.img`
- SHA-256: `d657365e5c14ba0bc1e93e9fc0ec38acb18f935e222d1bae66377011b96b9d15`
- Current untested output: `out/target/product/sycamore_row_5G/vendor_boot.img`
- SHA-256: `eb3388b6da20ce487976e40ff1a30ca8ee488680f01aeaaedb483e0aed555d18`
All images are exactly 67,108,864 bytes.
## Common header and retained sections
The local AOSP `unpack_bootimg.py` reports these identical values for all three:
- magic: `VNDRBOOT`
- header version: 4
- header size: 2,128
- page size: 4,096
- kernel load address: `0x40000000`
- ramdisk load address: `0x66f00000`
- tags load address: `0x47c80000`
- DTB load address: `0x47c80000`
- vendor cmdline: `bootopt=64S3,32N2,64N2`
- product name: empty
- bootconfig: empty (0 bytes; SHA-256 of empty content
`e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`)
- DTB: 192,119 bytes; SHA-256
`0e93b71aacf707a5fbb7a58eff73995b92d766a31a98d8d70e91f548c9e1d5af`
## Factory fragment table
| Index | Name | Type | Table offset | Image offset | Compressed size | Compression | board_id |
|---:|---|---|---:|---:|---:|---|---|
| 0 | empty | PLATFORM (1) | 0 | 4,096 | 27,422,144 | LZ4 legacy | 16 zero words |
Compressed payload SHA-256:
`06738f638a745075511c571347c5511331dda16b84d1ee88cda160c2dc7ebb14`
Uncompressed CPIO: 67,875,584 bytes; SHA-256
`cdf1d9fd1658989a4c198ca889802660f3a2d52a8fc527e40013c35da917642c`
Factory total vendor ramdisk size is 27,422,144 bytes. Its table is 108 bytes
with one entry. There is no RECOVERY entry and no DLKM entry.
## Hardware-tested V14 fragment table
| Index | Name | Type | Table offset | Image offset | Compressed size | Compression | board_id | Compressed SHA-256 | Uncompressed size | Uncompressed SHA-256 |
|---:|---|---|---:|---:|---:|---|---|---|---:|---|
| 0 | empty | PLATFORM (1) | 0 | 4,096 | 27,422,144 | LZ4 legacy | 16 zero words | `06738f638a745075511c571347c5511331dda16b84d1ee88cda160c2dc7ebb14` | 67,875,584 | `cdf1d9fd1658989a4c198ca889802660f3a2d52a8fc527e40013c35da917642c` |
| 1 | `recovery` | RECOVERY (2) | 27,422,144 | 27,426,240 | 31,391,394 | LZ4 legacy | 16 zero words | `ae0c2ab5adba18a2a5bdaa79760b4d8aa474bd2de9f82ba25a1a343e6d810467` | 69,037,312 | `9c3945cd24333659f8aea5bc78a01bd5d9c4762603ad4a943be4cb62a874d01f` |
Total vendor ramdisk size is 58,813,538 bytes. Its table is 216 bytes with
two entries, ordered PLATFORM then RECOVERY.
## Current output fragment table
| Index | Name | Type | Table offset | Image offset | Compressed size | Compression | board_id | Compressed SHA-256 | Uncompressed size | Uncompressed SHA-256 |
|---:|---|---|---:|---:|---:|---|---|---|---:|---|
| 0 | empty | PLATFORM (1) | 0 | 4,096 | 27,422,144 | LZ4 legacy | 16 zero words | `06738f638a745075511c571347c5511331dda16b84d1ee88cda160c2dc7ebb14` | 67,875,584 | `cdf1d9fd1658989a4c198ca889802660f3a2d52a8fc527e40013c35da917642c` |
| 1 | `recovery` | RECOVERY (2) | 27,422,144 | 27,426,240 | 31,677,990 | LZ4 legacy | 16 zero words | `b4e9de040d9d1c4e78a4fa8ab056a6bb7a42fafff6f99303f08fb43c54aa76ca` | 69,607,424 | `a1401628c4ca65d2fe1ab267e049d40c5e3a0c4d91187ad44d14b46808d2c001` |
Total vendor ramdisk size is 59,100,134 bytes. Its table is 216 bytes with
two entries, ordered PLATFORM then RECOVERY.
## Differential conclusion
- **A. Stock multi-fragment:** NO. It has a valid v4 table but only one entry.
- **B. Stock PLATFORM:** entry 0, unnamed, type 1.
- **C. Stock RECOVERY:** none. Stock recovery content is contained in PLATFORM.
- **D. Stock PLATFORM preserved by TWRP:** YES, byte-for-byte in both V14 and
current output, for both compressed and uncompressed payloads.
- **E. Stock non-recovery fragment regenerated/replaced:** NO. PLATFORM is the
only stock fragment and is exact. Stock has no DLKM or other fragment.
The hybrid principle is already implemented by
`tools/assemble_vendor_boot.sh`: the exact stock PLATFORM archive is entry 0 and
the generated TWRP ramdisk is appended as entry 1/RECOVERY. A new “hybrid” build
would not change this variable. PLATFORM preservation is therefore **PROVEN**
and is not a remaining explanation for the V14 cmd `0x18` failure.
The required aggregate/table changes are limited to total ramdisk size, table
size/count, downstream aligned section positions, and the AVB hash/footer.
V14 and current output retain the stock DTB, cmdline, bootconfig, core header
values, fragment-0 metadata, and fragment-0 bytes.
## Dynamic-partition corroboration
Read-only live recovery inspection showed liblp/TWRP preparing logical
partitions from `super` and mapping:
- `odm_dlkm_a` -> `dm-0`
- `product_a` -> `dm-1`
- `system_a` -> `dm-2`
- `system_b` -> `dm-3`
- `system_dlkm_a` -> `dm-4`
- `system_ext_a` -> `dm-5`
- `vendor_a` -> `dm-6`
- `vendor_dlkm_a` -> `dm-7`
The active-slot convenience links resolve `system`, `system_ext`, `product`,
`vendor`, `vendor_dlkm`, `odm_dlkm`, and `system_dlkm` to those mapper nodes.
Any resulting `/dev/block/by-name/<logical>` links are runtime-created links to
`dm-*`, not physical GPT partitions. The existing `recovery.fstab` correctly
uses `logical,first_stage_mount`; no invented physical by-name paths are needed.
No image was built, flashed, booted, or otherwise written to hardware during
this audit.

82
device.mk Normal file
View file

@ -0,0 +1,82 @@
DEVICE_PATH := device/motorola/sycamore_row_5G
PRODUCT_USE_DYNAMIC_PARTITIONS := true
ENABLE_VIRTUAL_AB := true
# This is the exact list exposed by ro.product.ab_ota_partitions on the live build.
AB_OTA_PARTITIONS := \
init_boot \
product \
system \
system_ext \
vendor
PRODUCT_SOONG_NAMESPACES += $(DEVICE_PATH)
# V16 boot-decrypt-unlock: expose the real FBE mode before teei_daemon starts.
# The stock daemon snapshots these read-only properties in its startup thread;
# setting them later from TWRP's partition scan leaves that thread waiting on
# the legacy vold.decrypt fallback instead of issuing the FBE unlock ioctl.
PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \
ro.product.device=XT2575-4 \
ro.crypto.state=encrypted \
ro.crypto.type=file
PRODUCT_COPY_FILES += \
$(DEVICE_PATH)/recovery.fstab:$(TARGET_COPY_OUT_RECOVERY)/root/system/etc/recovery.fstab \
$(DEVICE_PATH)/recovery/root/init.recovery.mt6835.rc:$(TARGET_COPY_OUT_RECOVERY)/root/init.recovery.mt6835.rc \
$(DEVICE_PATH)/recovery/root/init.recovery.mt8755.rc:$(TARGET_COPY_OUT_RECOVERY)/root/init.recovery.mt8755.rc \
$(DEVICE_PATH)/recovery/root/init.recovery.crypto.rc:$(TARGET_COPY_OUT_RECOVERY)/root/init.recovery.crypto.rc \
$(DEVICE_PATH)/recovery/root/system/etc/vintf/manifest.xml:$(TARGET_COPY_OUT_RECOVERY)/root/system/etc/vintf/manifest.xml \
$(DEVICE_PATH)/recovery/root/system/etc/vintf/manifest/android.hardware.health-service.example.xml:$(TARGET_COPY_OUT_RECOVERY)/root/system/etc/vintf/manifest/android.hardware.health-service.example.xml \
$(DEVICE_PATH)/recovery/root/vendor/manifest.xml:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/manifest.xml \
$(DEVICE_PATH)/recovery/root/vendor/etc/vintf/manifest/android.hardware.health-service.example.xml:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/etc/vintf/manifest/android.hardware.health-service.example.xml \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/0102030405060708090a0b0c0d0e0f10.ta:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/0102030405060708090a0b0c0d0e0f10.ta \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/020f0000000000000000000000000000.ta:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/020f0000000000000000000000000000.ta \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/06090000000000000000000000000000.ta:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/06090000000000000000000000000000.ta \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/08030000000000000000000000000000.ta:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/08030000000000000000000000000000.ta \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/08110000000000000000000000000000.ta:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/08110000000000000000000000000000.ta \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/40188311faf343488db888ad39496f9a.ta:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/40188311faf343488db888ad39496f9a.ta \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/5020170115e016302017012521300000.ta:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/5020170115e016302017012521300000.ta \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/5f7a5b3b29b041bca249524a031a00e3.ta:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/5f7a5b3b29b041bca249524a031a00e3.ta \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/8888c04fc30c4dd0a319ea29643d4d4c.ta:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/8888c04fc30c4dd0a319ea29643d4d4c.ta \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/abcd270ea5c44c58bcd3384a2fa2539e.ta:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/abcd270ea5c44c58bcd3384a2fa2539e.ta \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/c09c9c5daa504b78b0e46eda61556c3a.ta:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/c09c9c5daa504b78b0e46eda61556c3a.ta \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/c1882f2d885e4e13a8c8e2622461b2fa.ta:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/c1882f2d885e4e13a8c8e2622461b2fa.ta \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/d91f322ad5a441d5955110eda3272fc0.ta:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/d91f322ad5a441d5955110eda3272fc0.ta \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/e97c270ea5c44c58bcd3384a2fa2539e.ta:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/e97c270ea5c44c58bcd3384a2fa2539e.ta \
$(DEVICE_PATH)/recovery/root/vendor/thh/ta/isee_model.json:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/thh/ta/isee_model.json \
$(DEVICE_PATH)/recovery/root/vendor/etc/vintf/manifest/android.hardware.security.keymint-service.beanpod.xml:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/etc/vintf/manifest/android.hardware.security.keymint-service.beanpod.xml \
$(DEVICE_PATH)/recovery/root/vendor/etc/vintf/manifest/android.hardware.security.secureclock-service.beanpod.xml:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/etc/vintf/manifest/android.hardware.security.secureclock-service.beanpod.xml \
$(DEVICE_PATH)/recovery/root/vendor/etc/vintf/manifest/android.hardware.security.sharedsecret-service.beanpod.xml:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/etc/vintf/manifest/android.hardware.security.sharedsecret-service.beanpod.xml \
$(DEVICE_PATH)/recovery/root/vendor/etc/vintf/manifest/android.hardware.gatekeeper@1.0-service.xml:$(TARGET_COPY_OUT_RECOVERY)/root/vendor/etc/vintf/manifest/android.hardware.gatekeeper@1.0-service.xml
PRODUCT_PACKAGES += \
sycamore_gatekeeper_impl \
sycamore_gatekeeper_hidl \
sycamore_gatekeeper_service \
sycamore_gatekeeper_soft \
sycamore_gatekeeper_tee \
sycamore_keymint_ndk_v2 \
sycamore_keymint_private_utils \
sycamore_keymaster_messages \
sycamore_keymaster_portable \
sycamore_keymint_private \
sycamore_puresoft_keymaster \
sycamore_soft_attestation \
sycamore_cppbor_external \
sycamore_cppcose_rkp \
sycamore_keymint_service \
sycamore_secureclock_ndk_v1 \
sycamore_sharedsecret_ndk_v1 \
sycamore_tee_common \
sycamore_tee_imsg_log \
sycamore_tee_vfs \
sycamore_teei_daemon \
libsycamore_keytrace
PRODUCT_VENDOR_PROPERTIES += \
ro.vendor.mtk_ufs_support=1 \
ro.vendor.mtk_boot_devices=soc/112b0000.ufshci \
ro.hardware.gatekeeper=beanpod \
ro.crypto.volume.filenames_mode=aes-256-cts

298
diagnostics/keytrace.cpp Normal file
View file

@ -0,0 +1,298 @@
// Diagnostic-only Beanpod STORAGE_KEY boundary tracer.
// Logs only length and SHA-256, then calls the stock A16 implementation.
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <dlfcn.h>
#include <string>
#include <android/log.h>
#include <openssl/sha.h>
namespace {
constexpr const char* kSetKeyMaterialSymbol =
"_ZN9keymaster16ExportKeyRequest14SetKeyMaterialEPKvm";
constexpr const char* kGetPropertySymbol =
"_ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_";
// Microtrust uses the GlobalPlatform client ABI, but its headers are not
// shipped in this recovery tree. These declarations contain only the stable
// ABI prefix needed to inspect the four operation parameters. The 24-byte
// parameter union and 112-byte operation layout are also verified against the
// exact stock libTEECommon.so used by Beanpod.
struct TEEC_Context;
struct TEEC_Session;
struct TEEC_SharedMemory {
void* buffer;
size_t size;
};
struct TEEC_TempMemoryReference {
void* buffer;
size_t size;
};
struct TEEC_RegisteredMemoryReference {
TEEC_SharedMemory* parent;
size_t size;
size_t offset;
};
union TEEC_Parameter {
TEEC_TempMemoryReference tmpref;
TEEC_RegisteredMemoryReference memref;
uint8_t abi_size[24];
};
struct TEEC_Operation {
uint32_t started;
uint32_t paramTypes;
TEEC_Parameter params[4];
void* session;
};
static_assert(sizeof(TEEC_Parameter) == 24);
static_assert(sizeof(TEEC_Operation) == 112);
using TEEC_Result = uint32_t;
using InvokeCommand = TEEC_Result (*)(TEEC_Session*, uint32_t, TEEC_Operation*, uint32_t*);
constexpr uint32_t kTeecSuccess = 0;
constexpr uint32_t kTempInput = 0x5;
constexpr uint32_t kTempOutput = 0x6;
constexpr uint32_t kTempInout = 0x7;
constexpr uint32_t kWhole = 0xc;
constexpr uint32_t kPartialInput = 0xd;
constexpr uint32_t kPartialOutput = 0xe;
constexpr uint32_t kPartialInout = 0xf;
struct ByteRange {
const uint8_t* data = nullptr;
size_t size = 0;
bool found = false;
};
uint32_t ParamType(const TEEC_Operation* operation, size_t index) {
return (operation->paramTypes >> (index * 4)) & 0xf;
}
ByteRange FindInput(const TEEC_Operation* operation) {
if (operation == nullptr) return {};
for (size_t i = 0; i < 4; ++i) {
const uint32_t type = ParamType(operation, i);
if (type == kTempInput || type == kTempInout) {
const auto& ref = operation->params[i].tmpref;
return {static_cast<const uint8_t*>(ref.buffer), ref.size, true};
}
if (type == kWhole || type == kPartialInput || type == kPartialInout) {
const auto& ref = operation->params[i].memref;
if (ref.parent == nullptr) return {};
const size_t offset = type == kWhole ? 0 : ref.offset;
const size_t size = type == kWhole ? ref.parent->size : ref.size;
return {static_cast<const uint8_t*>(ref.parent->buffer) + offset, size, true};
}
}
return {};
}
ByteRange FindOutput(const TEEC_Operation* operation) {
if (operation == nullptr) return {};
for (size_t i = 0; i < 4; ++i) {
const uint32_t type = ParamType(operation, i);
if (type == kTempOutput || type == kTempInout) {
const auto& ref = operation->params[i].tmpref;
return {static_cast<const uint8_t*>(ref.buffer), ref.size, true};
}
if (type == kWhole || type == kPartialOutput || type == kPartialInout) {
const auto& ref = operation->params[i].memref;
if (ref.parent == nullptr) return {};
const size_t offset = type == kWhole ? 0 : ref.offset;
const size_t size = type == kWhole ? ref.parent->size : ref.size;
return {static_cast<const uint8_t*>(ref.parent->buffer) + offset, size, true};
}
}
return {};
}
void DigestHex(const uint8_t* data, size_t size, char (&hex)[SHA256_DIGEST_LENGTH * 2 + 1]) {
static constexpr uint8_t empty = 0;
uint8_t digest[SHA256_DIGEST_LENGTH];
SHA256(size == 0 ? &empty : data, size, digest);
static constexpr char digits[] = "0123456789abcdef";
for (size_t i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
hex[i * 2] = digits[digest[i] >> 4];
hex[i * 2 + 1] = digits[digest[i] & 0x0f];
}
hex[sizeof(hex) - 1] = '\0';
}
bool HasKeymasterError(uint32_t command) {
switch (command) {
case 0x04:
case 0x08:
case 0x0c:
case 0x18:
case 0x48:
case 0x4c:
case 0x50:
case 0x84:
case 0x340000:
return true;
default:
return false;
}
}
void LogDigest(const void* data, size_t size) {
if (size != 64) return;
static std::atomic_flag logged = ATOMIC_FLAG_INIT;
if (logged.test_and_set()) return;
uint8_t digest[SHA256_DIGEST_LENGTH];
SHA256(static_cast<const uint8_t*>(data), size, digest);
char hex[SHA256_DIGEST_LENGTH * 2 + 1];
static constexpr char digits[] = "0123456789abcdef";
for (size_t i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
hex[i * 2] = digits[digest[i] >> 4];
hex[i * 2 + 1] = digits[digest[i] & 0x0f];
}
hex[sizeof(hex) - 1] = '\0';
__android_log_print(ANDROID_LOG_INFO, "SYCA_DEKEY",
"SYCA_DEKEY_D len=%zu sha256=%s", size, hex);
}
} // namespace
// Diagnostic V13: interpose the single GlobalPlatform boundary used by both
// Beanpod request wrappers. This observes the registered-memory ranges but
// never writes to the operation, shared memory, result, or return-origin.
extern "C" TEEC_Result TEEC_InvokeCommand(TEEC_Session* session, uint32_t command,
TEEC_Operation* operation, uint32_t* return_origin) {
static const InvokeCommand original =
reinterpret_cast<InvokeCommand>(dlsym(RTLD_NEXT, "TEEC_InvokeCommand"));
if (original == nullptr) {
__android_log_print(ANDROID_LOG_FATAL, "SYCA_TA_TRACE",
"stock TEEC_InvokeCommand symbol unavailable");
__builtin_trap();
}
static std::atomic<uint64_t> next_sequence{0};
const uint64_t sequence = next_sequence.fetch_add(1, std::memory_order_relaxed) + 1;
const ByteRange input = FindInput(operation);
const ByteRange output_before = FindOutput(operation);
const uint8_t* input_data = input.found ? input.data : nullptr;
const size_t input_size = input.found ? input.size : 0;
char input_hash[SHA256_DIGEST_LENGTH * 2 + 1];
DigestHex(input_data, input_size, input_hash);
__android_log_print(ANDROID_LOG_INFO, "SYCA_TA_TRACE",
"SYCA_TA_SEQ=%llu session=0x%llx cmd=0x%x in_len=%zu "
"in_sha256=%s out_capacity=%zu",
static_cast<unsigned long long>(sequence),
static_cast<unsigned long long>(reinterpret_cast<uintptr_t>(session)),
command, input_size, input_hash,
output_before.found ? output_before.size : 0);
const TEEC_Result result = original(session, command, operation, return_origin);
const ByteRange output_after = FindOutput(operation);
// A failed TEEC transport did not return a serialized TA response. The
// memref can still retain its pre-call capacity, which must not be logged
// or hashed as though it were returned data.
const bool has_response = result == kTeecSuccess && output_after.found;
const uint8_t* output_data = has_response ? output_after.data : nullptr;
const size_t output_size = has_response ? output_after.size : 0;
char output_hash[SHA256_DIGEST_LENGTH * 2 + 1];
DigestHex(output_data, output_size, output_hash);
char origin[16];
if (return_origin == nullptr) {
memcpy(origin, "NA", 3);
} else {
snprintf(origin, sizeof(origin), "%u", *return_origin);
}
if (result == kTeecSuccess && HasKeymasterError(command) && output_size >= sizeof(int32_t)) {
int32_t keymaster_error;
memcpy(&keymaster_error, output_data, sizeof(keymaster_error));
__android_log_print(ANDROID_LOG_INFO, "SYCA_TA_TRACE",
"SYCA_TA_RET_SEQ=%llu cmd=0x%x teec_ret=%u origin=%s out_len=%zu "
"out_sha256=%s km_error=%d",
static_cast<unsigned long long>(sequence), command, result, origin,
output_size, output_hash, keymaster_error);
} else {
__android_log_print(ANDROID_LOG_INFO, "SYCA_TA_TRACE",
"SYCA_TA_RET_SEQ=%llu cmd=0x%x teec_ret=%u origin=%s out_len=%zu "
"out_sha256=%s",
static_cast<unsigned long long>(sequence), command, result, origin,
output_size, output_hash);
}
return result;
}
namespace android::base {
// V10-IDENTITY one-variable experiment. Beanpod obtains these two fields
// through android::base::GetProperty() while constructing BPConfigureRequest
// for command 0x48. Override only these Beanpod-facing lookups and delegate
// every other property unchanged.
std::string GetProperty(const std::string& key, const std::string& default_value) {
if (key == "ro.product.name" || key == "ro.product.device") {
constexpr const char* kStockIdentity = "XT2575-4";
__android_log_print(ANDROID_LOG_INFO, "SYCA_IDENTITY",
"%s=%s", key.c_str(), kStockIdentity);
return kStockIdentity;
}
using Original = std::string (*)(const std::string&, const std::string&);
static const Original original =
reinterpret_cast<Original>(dlsym(RTLD_NEXT, kGetPropertySymbol));
if (original == nullptr) {
__android_log_print(ANDROID_LOG_FATAL, "SYCA_IDENTITY",
"stock GetProperty symbol unavailable");
__builtin_trap();
}
return original(key, default_value);
}
} // namespace android::base
namespace keymaster {
// V9-OSVERSION one-variable experiment. Beanpod dynamically imports this
// helper when building BPConfigureRequest for command 0x48. Keep recovery's
// global Android 12 properties intact and change only the value observed by
// this Beanpod process.
uint32_t GetOsVersion() {
constexpr uint32_t kAndroid16OsVersion = 160000;
__android_log_print(ANDROID_LOG_INFO, "SYCA_OSVERSION",
"Beanpod ConfigDeviceInfo os_version=%u",
kAndroid16OsVersion);
return kAndroid16OsVersion;
}
class ExportKeyRequest {
public:
void SetKeyMaterial(const void* key_material, size_t length);
};
void ExportKeyRequest::SetKeyMaterial(const void* key_material, size_t length) {
using Original = void (*)(ExportKeyRequest*, const void*, size_t);
static const Original original = reinterpret_cast<Original>(
dlsym(RTLD_NEXT, kSetKeyMaterialSymbol));
LogDigest(key_material, length);
if (original == nullptr) {
__android_log_print(ANDROID_LOG_FATAL, "SYCA_DEKEY",
"stock SetKeyMaterial symbol unavailable");
__builtin_trap();
}
original(this, key_material, length);
}
} // namespace keymaster

BIN
prebuilt/dtb/mt8755.dtb Normal file

Binary file not shown.

BIN
prebuilt/dtbo.img Normal file

Binary file not shown.

BIN
prebuilt/kernel Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
prebuilt/modules/aee_aed.ko Normal file

Binary file not shown.

Binary file not shown.

BIN
prebuilt/modules/aee_rs.ko Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
prebuilt/modules/clkbuf.ko Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
prebuilt/modules/cqhci.ko Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
prebuilt/modules/emi-mpu.ko Normal file

Binary file not shown.

BIN
prebuilt/modules/emi.ko Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
prebuilt/modules/fusb304.ko Normal file

Binary file not shown.

BIN
prebuilt/modules/gt9896s.ko Normal file

Binary file not shown.

BIN
prebuilt/modules/hqsysfs.ko Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
prebuilt/modules/irq-dbg.ko Normal file

Binary file not shown.

BIN
prebuilt/modules/isee.ko Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
prebuilt/modules/mm8013.ko Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
prebuilt/modules/mrdump.ko Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
prebuilt/modules/mt6375.ko Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more