RK3576 first-stage loader: external (KMS-style) signing test

2026-11-03 · Wilber Zheng · host-only test · no RK board, no real keys, no cloud KMS used · throwaway test keys, deleted afterwards

Result: the RK3576 loader and idblock can be signed the same way Frank did for Amlogic: the signing tool only sees the public key, and a 32-byte digest goes out to KMS for signing.
rk_sign_tool (rkbin, v1.42) has a built-in extract/inject mode (ss --extract / ss --inject). A loader signed externally is byte-identical to one signed by the tool with a local key file, except for the signature fields and a 4-byte checksum. The tool's own verification (vl / vb) passes on it.

Approaches considered

Flow

1. Extract  rk_sign_tool lk --pubkey pub.pem        (public key only)
            rk_sign_tool ss --extract
            rk_sign_tool sl --loader loader.bin    -> out/si_usb_head.bin, si_flash_head.bin (32-byte SHA-256 each)
            rk_sign_tool sb --idb idblock.img      -> out/si_idb_head.bin
2. Sign     each digest -> KMS asymmetricSign (RSA-PSS, SHA-256, salt 32)
            verify each signature with the public key, then write it back over the same file
3. Inject   rk_sign_tool ss --inject
            rk_sign_tool sl --loader loader.bin
            rk_sign_tool sb --idb idblock.img
4. Check    rk_sign_tool vl / vb, then otp --hash (public-key hash for OTP)

Tests with a local key file

#TestResult
0Build an unsigned loader and idblock with rkbin boot_merger and RK3576MINIALL.iniDone
ABaseline: rk_sign_tool signs with a test key file; check with vl / vbPass
BExternal: public key only, extract 3 digests, sign them outside the tool with openssl, inject; check with vl / vbPass
A vs BByte compare of both loaders and idblocksMatch Differences only in the signature fields (two in the loader, one in the idblock) and the loader's trailing 4-byte checksum
OTPotp --hash on both loadersIdentical
PSSSignatures produced by the tool itself, checked with plain opensslConfirmed RSA-PSS, SHA-256, salt length 32. Signatures are stored byte-reversed; the tool handles that on inject
CNegative: sign with a wrong key, injectAs expected Inject accepts it silently (rc 0); vl / vb fail ("failed to check signature")
4096A, B and C rerun with a 4096-bit keyPass Same results as 2048-bit

Full flow against a simulated Cloud KMS

#TestResult
1Normal run, 4096-bit keyPass All 3 signatures verified before inject; vl / vb pass; OTP hash produced. Audit log: exactly 1 getPublicKey and 3 asymmetricSign calls
2KMS signs with a different key (misconfigured key)Fails as expected First signature fails the public-key check; nothing is injected
3Wrong token (no permission)Fails as expected KMS returns 401; the flow stops

To switch to real Cloud KMS, only the endpoint (https://cloudkms.googleapis.com/v1/projects/…/cryptoKeyVersions/1) and the token (gcloud auth print-access-token) change.

Notes

Comparison with Amlogic

Amlogic (Frank)RK3576 (this test)
External-sign interfaceacpu-imagetool --cmd-signpipe-* (pipe, request/response)rk_sign_tool ss --extract / --inject (files)
Sent to KMS32-byte digest32-byte digest
AlgorithmRSA-PSS-4096, SHA-256RSA-PSS, SHA-256, salt 32 (2048 and 4096 tested)
Tool accepts a wrong signatureYes, hence pipe-verifyYes, hence verify before and after inject

Sources