0.2.4: the trustlet comes from the active slot, unpinned, and a refusal says why
All checks were successful
package / package (push) Successful in 1m22s
All checks were successful
package / package (push) Successful in 1m22s
0.2.3's manifest pinned the sha256 of one Android build's focal64 (16.82.0, the build the dev phone runs). Fairphone re-signs the trustlet every release, so that pin matched exactly one of the six builds seen, and two of two field reports had no sensor: one user on 16.100.0 edited the manifest by hand, another ended up with a file QTEE refuses. The manifest now carries '-' instead of a hash and depends on fp6-vendor-blobs 1-r3, which tries the active slot first and verifies the image's structure; QTEE's signature check is the gate it always was (one flipped byte -> ERROR_ELF_SIGNATURE_ERROR, measured 2026-09-03 and again today). post-install reassembles the trustlet right away, so 'apk add' no longer needs a boot for it. post-upgrade re-derives it from the active slot, which replaces a hand-placed or wrongly pinned file, and then restarts the daemon -- a plain restart, so a daemon that exited on a refused trustlet comes back up on the re-derived one. loadFromBuffer failures name the loader's verdict. The field's first report was a bare result=12; it now reads ERROR_ELF_SIGNATURE_ERROR with what to do about it. Probed on the phone with this build: a one-byte tampered image and 100000 random bytes both print it, the pristine image loads, and the suites pass 8/8.
This commit is contained in:
parent
845c658786
commit
823c710b15
6 changed files with 76 additions and 24 deletions
|
|
@ -68,7 +68,7 @@ namespace {
|
|||
|
||||
// Bumping this is what publishes a package: the registry answers 409 for a
|
||||
// version it already has, which a build treats as a no-op.
|
||||
constexpr const char* Version = "0.2.3";
|
||||
constexpr const char* Version = "0.2.4";
|
||||
|
||||
bool g_verbose = false;
|
||||
// 500 ms was the research harness's pace, chosen so a human could read the
|
||||
|
|
@ -952,6 +952,24 @@ void UnloadStale(qcomtee_object* loader) {
|
|||
qcomtee_object_refs_dec(p[2].object);
|
||||
}
|
||||
|
||||
// The loader's verdict on the image itself (IQSEEComCompatAppLoader.h in
|
||||
// Qualcomm's securemsm-kernel), for the codes a phone in the field has produced
|
||||
// or plausibly will. Named because the first field report was a bare 12: QTEE
|
||||
// refusing an image whose signature or per-segment hashes do not check out,
|
||||
// which on this device means a trustlet that is not the active slot's own.
|
||||
const char* LoaderVerdict(qcomtee_result_t result) {
|
||||
switch (result) {
|
||||
case 11: return " ERROR_PIL_ROLLBACK_FAILURE: the image is older than the anti-rollback state allows";
|
||||
case 12: return " ERROR_ELF_SIGNATURE_ERROR: QTEE refused the image's signature or segment hashes;"
|
||||
" the trustlet is damaged or not this slot's build. Re-derive it from the"
|
||||
" active slot: /usr/lib/fp6-vendor-blobs/extract --refresh";
|
||||
case 13: return " ERROR_METADATA_INVALID: not a QTEE trustlet image";
|
||||
case 16: return " ERROR_ALREADY_LOADED";
|
||||
case 28: return " ERROR_ELF_LOADING";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
qcomtee_object* LoadTrustlet(qcomtee_object* loader, const std::string& path) {
|
||||
UnloadStale(loader);
|
||||
|
||||
|
|
@ -981,8 +999,8 @@ qcomtee_object* LoadTrustlet(qcomtee_object* loader, const std::string& path) {
|
|||
p[3].attr = QCOMTEE_OBJREF_OUTPUT;
|
||||
qcomtee_result_t result = 0;
|
||||
if (qcomtee_object_invoke(loader, 1, p, 4, &result) || result) {
|
||||
std::println(std::cerr, "loadFromBuffer failed, result={}",
|
||||
static_cast<int>(result));
|
||||
std::println(std::cerr, "loadFromBuffer failed, result={}{}",
|
||||
static_cast<int>(result), LoaderVerdict(result));
|
||||
return QCOMTEE_OBJECT_NULL;
|
||||
}
|
||||
std::println("trustlet loaded from {} ({} bytes), distName='{}'", path,
|
||||
|
|
|
|||
Loading…
Reference in a new issue