3.2. Secure boot and image signing

The Onyx Boox Note Air5 C carries a fully populated, correctly signed Android Verified Boot (AVB) layer and devinfo reports the bootloader locked — but the Qualcomm bootloader chain underneath it (XBL, ABL, TZ) is signed with Qualcomm’s publicly known generic test keys, not an OEM production key. A device running test-key-signed XBL has its Secure Boot fuse (QFPROM SEC_BOOT) effectively open — not blown, or blown to a non-exclusive/test key (see On-device confirmation below). Either way the ROM pins the chain to no secret, so the whole trust chain — including the AVB enforcement ABL itself performs — is bypassable by re-signing images with the same, publicly available test key: the re-signing acceptance itself has been confirmed on-device, though flashing a patched ABL specifically to defeat its checks has not (see Why verification is not enforced below).

3.2.1. On-device confirmation

A foreign Firehose programmer (lenovo_motorola …[REDACTED]…_fhprg.bin, HWID 0x0) loaded and dumped the device. On a secure-booted device, PBL/Sahara rejects a non-OEM-signed programmer against the fused OEM_PK_HASH; it did not. SEC_BOOT is open — either not blown, or set to a non-exclusive/test key. See Firehose fuse read and secure-boot confirmation.

QFPROM SEC_BOOT_ENABLE / OEM_PK_HASH are memory-mapped registers at 0x00780000, not in any partition. Sahara reports OEM_ID=0x0000 / MODEL_ID=0x0000 (see EDL / Firehose interface); what has and hasn’t been read from these two registers is under Not yet observed below.

PBL enforces the ELF per-segment SHA-384 hash table but not the root-of-trust signature: a modified image is accepted iff its hash table is recomputed to match (no OEM re-signing needed); a zeroed hash table is rejected at Sahara. Integrity (SHA-384) is enforced; authenticity (OEM_PK_HASH signature) is not. Details in Firehose fuse read and secure-boot confirmation.

3.2.2. AVB / vbmeta_a.bin

The AVB0 header, its descriptors, and the recomputed image hashes:

Field

Value

Magic / libavb

AVB0, required 1.0, avbtool 1.3.0

Algorithm

2 = SHA256_RSA4096 (signed)

Signature

present, 512 bytes; embedded 4096-bit RSA public key

Public key SHA256

7728e30f50bfa5cea165f473175a08803f6a8346642b5aa10913e9d9e6defef6

Flags

0x00000000

→ VERIFICATION_DISABLED (bit0)

False (verification NOT disabled)

→ HASHTREE_DISABLED (bit1)

False (dm-verity NOT disabled)

Build fingerprint

Onyx/NoteAir5C/NoteAir5C:11/2026-08-03_16-13_4.2.1-rel_0803_eaa32b54f/3268:user/release-keys

Security patch

2025-10-01, Android 11

Descriptors:

  • Chain partition → vbmeta_system (rollback_loc 2, its own 2048-bit key) — signature delegated.

  • Hash descriptors (whole-image): boot, dtbo, recovery.

  • Hashtree descriptors (dm-verity): 2 (system/vendor/odm/product class partitions).

Integrity re-verification (digest = SHA256(salt ‖ image[:image_size])):

Partition

AVB descriptor digest

Recomputed

Match

boot

95fcfc2751e6006381a6c9e4…

same

yes

dtbo

7a108e1f0ac3296cf72daa77…

same

yes

recovery

694d0092478bd48505081cea…

same

yes

The AVB layer is legitimately signed with release-keys, verification and hashtree are enabled, and the boot/dtbo/recovery images match their descriptors exactly. At the AVB level this is a properly verified production build. The ABL binary that enforces this layer is itself unprotected (see Why verification is not enforced), so a patched ABL can report success regardless of the actual image contents.

3.2.3. Device lock state — devinfo.bin

devinfo.bin’s is_unlocked field reads 0 → LOCKED (fastboot flashing disabled in normal flow); full struct layout and byte offsets are in AVB enforcement code path (LoadImageAndAuth / libavb). A locked flag only matters if the code reading it is itself trusted, which it is not here (see Why verification is not enforced).

3.2.4. Qualcomm bootloader signing chains

The embedded X.509 chains (Qualcomm MBN / SecTools format) are the actual root of trust. xbl_a.bin, abl_a.bin and tz_a.bin are all signed with the same generic Qualcomm TEST chain:

Tier

Subject

Note

Leaf

CN=SecTools Test User, O=SecTools

image signer

Attestation CA

CN=Generated Test Attestation CA, O=SecTools

Root CA

CN=Generated Test Root CA, OU=CDMA Technologies, OU=General Use Test Key (for testing only)

public test key

  • Signature algorithm: ECDSA P-384 (secp384r1) / SHA-384.

  • XBL and ABL chain to the identical test root: SHA256 fpr 95:9B:8D:05:49:EF:41:BE:FA:BC:24:F5:1E:FE:84:FE:E3:66:AC:16:9A:B0:4A:0D:B3:0C:79:9B:32:4F:D7:98.

  • “General Use Test Key (for testing only)” is Qualcomm’s shipped-in-SecTools sample key; the private half is effectively public knowledge.

tz_a.bin also embeds a genuine Qualcomm chain CN=CASS - SBL3 (QUALCOMM)QSEE Attestation Root CA 4 SubCA 1QSEE Attestation Root CA 4 (Qualcomm Technologies, Inc. / Qualcomm Cryptographic Operations). These are the QSEE/Keymaster key-attestation keybox certificates carried as reference data inside TrustZone. They are real Qualcomm production certs but have nothing to do with verifying the boot images, and are not the signing chain.

3.2.5. Multi-image OEM authentication table (multiimgoem_a)

multiimgoem_a.bin (LUN4, 32 KB) carries a second, independent signing mechanism alongside the per-image MBN hash segments above: a multi-image OEM authentication table. MBN/ELF32 envelope (test-key cert chain, same 2024-07-03 attestation cert as imagefv_a) wrapping a 1112-byte data segment loaded to IMEM 0x148d7000. Layout: ASCII magic ``MULT`` at +0, a reserved zero region, an entry count = 15 (0x0f at +0x108), then 14 fixed 56-byte records starting at +0x118, each [SHA-384 : 48 B][image-id : u64 LE] (the image-id is the last 8 bytes of each record). The header’s entry count of 15 has no matching 15th record: 0x118 + 15×56 = 1120 bytes, one past the segment’s 1112-byte end, so only 14 complete records physically fit. The image-ids present are 0x02, 0x03, 0x04, 0x05, 0x07, 0x0e, 0x12, 0x15, 0x17, 0x21, 0x24, 0x25, 0x27, 0x28 (QTI multi-image image-IDs; the exact id→partition-name mapping is a QTI enum not in this dump).

So this is a QTI “multi-image” descriptor: one signed table of 14 SHA-384 image hashes that lets ABL/XBL authenticate that group of images under a single OEM signature rather than requiring each to carry its own MBN signature — using the same hash algorithm (SHA-384) as the rest of the chain. The companion multiimgqti slot is not present on this unit; only multiimgoem is. A/B slots are byte-identical.

3.2.6. Why verification is not enforced

The immutable PBL verifies XBL against OEM_PK_HASH only when SEC_BOOT is blown; since XBL/ABL here are signed by the generic public test key (Qualcomm bootloader signing chains above), SEC_BOOT is effectively open — not blown, or blown to a non-exclusive/test key. The direct consequence for each enforcement point documented above: ABL’s devinfo lock check (Device lock state — devinfo.bin) and its AVB signature check (AVB / vbmeta_a.bin) both run inside an ABL binary that anyone can re-sign with the same public test key, so both are bypassable in practice — the same re-signing acceptance already confirmed on-device via the Firehose programmer above (On-device confirmation), though turning off the ABL-side checks specifically has not been directly tested by flashing a patched ABL.

3.2.7. Not yet observed

3.2.8. Provenance

Source:

static EDL dump in ../_READONLY/ (read-only); on-device EDL test sessions.

Method:

AVB parsed with a hand-rolled AVB0 reader (no avbtool on host); hashes recomputed with Python hashlib. Certs carved on DER 30 82 boundaries from xbl_a / abl_a / tz_a, decoded with openssl x509 / asn1parse. Lock state read from devinfo.bin offsets after the 13-byte ANDROID-BOOT! magic. Carved DER certs kept in /tmp/boox_certs/ for re-inspection.

Cross-refs:

Firehose fuse read and secure-boot confirmation (on-device confirmation, hash gate), EDL / Firehose interface (Sahara OEM_ID, OEM_PK_HASH), AVB enforcement code path (LoadImageAndAuth / libavb) (device_info accessors), Partition map and checksums.