# 009 — Userspace hardening / signing / AVB checklist (Gate G) **Task:** T040. **Companion:** kernel half = `docs/009-gate-g-kernel-attribution.md`. **Policy (2026-09-29):** project **release keys yes**; **bootloader relock no** (AXP/LOS unlocked norm; IOdes-class lock = non-goal). See `specs/009-axp-los23-port/research.md`. Secrets never enter Git. Host material lives under `/srv/rom/secrets/` on `rom-dev01` only. --- ## Measured baseline (2026-09-29) | Prop / fact | GOLDEN (AXP Pro) | LAB (Stage1 pack) | Target | |-------------|------------------|-------------------|--------| | `ro.build.type` | `user` | `userdebug` | `user` for ship; `userdebug` OK while developing | | `ro.build.tags` | `release-keys` | `test-keys` | `release-keys` | | `ro.build.flavor` | `lineage_sunfish-user` | `lineage_sunfish-userdebug` | `…-user` at ship | | `ro.debuggable` | `0` | `0` on LAB measure wait — typically `1` on userdebug | `0` on ship | | `ro.secure` | `1` | (LOS default) | `1` | | `ro.boot.flash.locked` | `0` | `0` | `0` (unlocked — intentional) | | `ro.boot.verifiedbootstate` | `orange` | `orange` | `orange` while unlocked | | `ro.boot.vbmeta.device_state` | `unlocked` | (same class) | unlocked | | `ro.boot.veritymode` | `enforcing` | expect enforcing | keep | | SELinux | Enforcing (expect) | **Enforcing** | keep | | OTA zip cert | AXP releasekey | **AOSP `testkey.x509.pem`** (byte-match on stage1 zip) | project releasekey | | AVB signing key in tree | AXP custom | `external/avb/test/data/testkey_rsa2048.pem` | project AVB key | | `BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS --flags 3` | AXP sets real AVB when enabled | LOS sunfish adds `--flags 3` unless `WITH_AVB=true` | keep flags 3 (signing infra only; not runtime harden) | LAB adb sample (device `11231JEC207477`, Stage1 sideload): `test-keys` / `userdebug` / `orange` / `flash.locked=0` / `getenforce=Enforcing`. GOLDEN source: `/srv/rom/references/axp/golden/2026-09-13/getprop.txt`. --- ## What “done” means for Gate G userspace 1. Ship images/OTAs verify as **`release-keys`** (not AOSP test-keys). 2. Phonesky / microG keep **their** project JKS identities under `/srv/rom/secrets/{phonesky,microg}-signing/` (already exist). 3. AVB *build* uses project keys where LOS23/sunfish allows; device stays **unlocked** (`orange`). Relock is **out of scope**. 4. Userspace hardening stages are attributable (patch dir + map) or explicitly deferred/skipped with reason. 5. Gaps written down — no silent omit. --- ## Stages (do in order) ### US-S0 — Inventory (this doc) — **done 2026-09-29** - [x] Compare GOLDEN vs LAB signing/AVB props. - [x] Confirm stage1 zip OTA cert == AOSP testkey. - [x] Confirm no `~/.android-certs` / platform release keys on host yet (only Phonesky + microG JKS). - [x] Record bootloader-lock = wont. ### US-S1 — Generate project Android signing keys — **done 2026-09-29 (host)** Host-only. Never commit `.pk8` / private PEM. | Key | Role | |-----|------| | `releasekey` | Default APK/OTA (`PRODUCT_DEFAULT_DEV_CERTIFICATE`) | | `platform` | `platform`-signed priv-apps | | `shared` / `media` / `networkstack` / `sdk_sandbox` / `bluetooth` / `nfc` | AOSP partition signing set | Procedure: `scripts/009-generate-release-keys.sh` on `rom-dev01` → prefers `/srv/rom/secrets/android-certs/` if writable, else `$HOME/.android-certs` (Lineage default; used 2026-09-29 because secrets/ is root-owned 750 without group write). **Mandatory before first signed ship bacon:** encrypted/offline backup of that directory (constitution). Public `*.x509.pem` may later be copied into Git for docs; private material must not. Acceptance: - [x] `$HOME/.android-certs/{releasekey,platform,…}.{pk8,x509.pem}` + `avb.pem` exist on `rom-dev01` (2026-09-29T16:37Z) - [x] Host tar backup: `/home/android-build/backups/android-certs-20260929T1637Z.tar.gz` (mode 600; SHA256 in `.sha256`) — **still copy offline/encrypted** - [x] `releasekey` DN = `O=AXP-Resurection, OU=sunfish-a16, CN=release` (not AOSP testkey) - [ ] Root later: `install -d` `/srv/rom/secrets/android-certs` and move keys there - [ ] Owner offline encrypted copy confirmed ### US-S2 — Wire release keys into bacon — **in progress** Lineage convention: `PRODUCT_DEFAULT_DEV_CERTIFICATE` → path without suffix (e.g. `…/releasekey`). On LOS 23 / soong, **env export alone is ignored** — must be a product makefile `PRODUCT_` (see `vendor/sunfish-a16/signing.mk`, inherited via `magisk.mk`). Verified `get_build_var` → project releasekey. Keys **present** on rom-dev01 (`$HOME/.android-certs/releasekey.pk8`, 2026-09-29). First artifact `20261001T221102Z-…-cve-hi-releasekeys` was **still AOSP testkey** (env-only wire). Rebacon after `signing.mk` (2026-10-01T22:12Z). CVE LAB bacons keep test-keys. First real signed bacon stays **`userdebug`**. **LAB 2026-10-02:** first signed sideload booted Wi‑Fi OK but Bluetooth crash-looped — zygote `selinux_android_setcontext` failed with `seinfo=default` for `com.android.bluetooth`. Cause: APK signed with project `certs/bluetooth.pk8` while `MAINLINE_BLUETOOTH_SEPOLICY_DEV_CERTIFICATES` stayed on AOSP `build/make/target/product/security/` (non-`com.google.android.bt` default in `config.mk`). Fix: `PRODUCT_MAINLINE_BLUETOOTH_SEPOLICY_DEV_CERTIFICATES` in `signing.mk` (rebacon required; boot-only flash insufficient). Acceptance: - [ ] Zip `META-INF/com/android/otacert` ≠ AOSP testkey - [ ] `ro.build.tags=release-keys` on LAB after **sideload** (boot-only flash insufficient) (note: first RK sideload still showed `dev-keys` on userdebug — recheck after BT fix) - [x] Magisk preroot + microG + Wi‑Fi **and Bluetooth** OK on Stage1+combo4+CVE-hi ship pack (`20261002T085915Z-…-cve-hi-releasekeys`, LAB 2026-10-02: BT ON, crashed 0) - [x] `plat_mac_permissions.xml` embeds project `bluetooth.x509.pem` (`seinfo=bluetooth`) - [ ] Minor: LOS SetupWizard after Wi‑Fi connect can bounce to language step (Wi‑Fi stays connected; non-blocking; note only) - [ ] Prior test-keys image retained for bisect (`20261001T201836Z-…-cve-hi-batch2` / `#49`, or hi-batch3 when OK) Script: `scripts/009-bacon-axp-stage1-combo4-cve-hi-releasekeys.sh` + `vendor/sunfish-a16/signing.mk` → `vendor/sunfish-a16/certs/releasekey` (in-tree copy of `$HOME/.android-certs`; **gitignored** — soong rejects paths outside `ANDROID_BUILD_TOP`). ### US-S3 — Project AVB key (unlocked device) Today sunfish often signs with AOSP `testkey_rsa4096` (or rsa2048) and adds `--flags 3` unless `WITH_AVB=true` (`BoardConfigLineage.mk` / `BoardConfig-common.mk`). LAB `20261002T085915Z` vbmeta = AOSP testkey_rsa4096 + flags 3 (not project `avb.pem`). AXP ships custom AVB keys and documents AVB2; GOLDEN stays **unlocked/orange**. Clone that *signing* posture, not IOdes lock. **Bootloader / OEM unlocking (2026-10-02):** relock remains **wont** for this Magisk track (orange + preroot). Locking with Magisk-patched boot is a brick risk on Pixel unless the full AVB chain is enforced end-to-end — out of scope. Keep `flash.locked=0` / `verifiedbootstate=orange`. Prefer leaving **OEM unlocking** enabled in Developer options as a *best-effort* escape hatch for future unlock attempts — it is **not** a guarantee of recovery, data preservation, or rescue from every failure mode (AOSP: unlock ability is conditional). Do **not** turn it off casually. Wire: `vendor/sunfish-a16/BoardConfig.mk` overrides `BOARD_AVB_*` → `vendor/sunfish-a16/certs/avb.pem` + `SHA256_RSA4096`. Leave `WITH_AVB` unset so `--flags 3` stays (Magisk-friendly). **What flags 3 means (libavb):** `HASHTREE_DISABLED (1) | VERIFICATION_DISABLED (2)`. Descriptors are not enforced at boot; this is **AVB signing infrastructure**, not “AVB-hardened verified boot”. Do not call US-S3 “AVB endurecido” while flags stay 3. A later stage may measure `WITH_AVB=true` (flags 0) + Magisk resign — separate acceptance. Acceptance: - [x] `BOARD_AVB_KEY_PATH` → project `vendor/sunfish-a16/certs/avb.pem` - [x] Artifact gates **fail closed** on **target_files/IMAGES** (not OUT make images): pubkey sha1 == `avb_pkmd.bin` (≠ AOSP `2597c218…`); Flags `3`; `verify_image --key` vbmeta + `--expected_chain_partition` vbmeta_system; Magisk boot hash matches vbmeta; `magiskboot cpio test` ≠ 0 - [x] Document clearly: runtime AVB enforce = **off** (flags 3) — not “AVB endurecido” - [x] Device still boots unlocked; Magisk preroot intact (LAB 2026-10-02 sideload + boot/vbmeta reinforce; `magiskd`, `flash.locked=0`, orange) - [x] No bootloader relock; OEM unlocking left enabled (escape hatch, not guarantee) ### Branding (no rebacon-only) - [x] Wire `vendor/sunfish-a16/branding.mk` → `TARGET_UNOFFICIAL_BUILD_ID=AXP-Resurection-Pro` (full Magisk track, not slim; next bacon embeds it in `ro.lineage.version`) - [x] Ship script publishes sibling `AXP-Resurection-Pro-*-sunfish.zip` (hardlink/copy of Lineage bacon zip). Current artifact renamed the same way without rebuild. ### US-S4 — Userspace hardening (first attributable pack) See `docs/009-us-s4-userspace-hardening.md`. - [x] Stage 0 LAB measure (Enforcing + FBE) - [x] US-S4-1: strip Openssh/rsync/procmem packages from Lineage `common.mk` (`apply_009_drop_openssh_packages.py`); keep `init.openssh.rc` (AXP PRO) - [x] LAB 2026-10-02: ship `20261002T151944Z` sideloaded + reinforced; see `docs/009-lab-acceptance-20261002-uss4.md` - [ ] **US-S4-2** (wired, pending bacon): drop Jelly + Gallery2 (AXP PRO product oracle — Fossify stays); `apply_009_drop_jelly_gallery2.py` + `NoLineageJelly`; see `docs/009-axp-pro-product-apps-20260324.md` | Candidate | Priority | Notes | |-----------|----------|-------| | Confirm SELinux Enforcing + encryption | **done** | LAB Enforcing + FBE | | ptrace/Yama | **done US-S5-1** | LAB `162840Z` `CONFIG_SECURITY_YAMA=y` | | Debug/bugreport surface trim | **done US-S4-1** | AXP PRO product oracle | | Jelly + Gallery2 debloat | **US-S4-2 wired** | AXP PRO product; Fossify kept | | mount flags / noexec where stock allows | early | don't break HW | | hardened_malloc | later | Graphene/AXP; portability cost | | Broad Graphene framework (scopes, etc.) | later / deferred | high UX cost; not Magisk-clone MVP | Ship patches under `vendor/sunfish-a16/patches/009-axp-los23-port/hardening/` with README attribution. One stage per bacon when risk is unclear. ### US-S5 — Kernel CVE mega / more Graphene (on boot-OK pack) **Do not** flash old `20260918*-axp-kh-cve` / `kh-full` (logo hang). - [x] CVE clean-offset 429 + hi-batch1–3 on Magisk ship (`151944Z` LAB OK) - [x] **US-S5-1** LAB OK (`162840Z`): `CONFIG_SECURITY_YAMA=y` + `CONFIG_SLAB_FREELIST_RANDOM=y` — see `docs/009-lab-acceptance-20261002-uss5.md` - [ ] Optional later: `CVE-2022-49980` lock-scope port; Stage1 slab FAIL retries; `hardened_malloc` Map updates: `docs/009-kernel-hardening-map.md` + Gate G attribution. ### US-S6 — Optional later - [ ] Switch default lunch to `lineage_sunfish-user` for ship - [ ] GPG-sign published SHA sums (AXP does; nice-to-have) - [ ] LOS23 nightly rebase (vanilla + re-apply Magisk/stage1/keys) - [ ] Bootloader lock — **wont** --- ## Host paths | What | Path | |------|------| | Platform / OTA / AVB secrets | `$HOME/.android-certs/` on rom-dev01 (2026-09-29); prefer `/srv/rom/secrets/android-certs/` after root relocate | | Certs tar backup | `/home/android-build/backups/android-certs-20260929T1637Z.tar.gz` | | Phonesky JKS | `/srv/rom/secrets/phonesky-signing/` | | microG JKS | `/srv/rom/secrets/microg-signing/` | | Keygen script (repo) | `scripts/009-generate-release-keys.sh` | | Signing README (vendor) | `vendor/sunfish-a16/patches/009-axp-los23-port/signing/README.md` | | GOLDEN props | `/srv/rom/references/axp/golden/2026-09-13/` | | AXP docs (mirror) | `…/axpos.org/docs/knowledge/{signatures,verifiedboot}/` | --- ## Explicit non-goals - Bootloader relock / green verified-boot for end users. - Replacing Phonesky/microG keys with platform keys. - Committing private keys or passphrases to Git. - Treating missing lock as Gate G failure.