Repository navigation
Delete the RKP-only properties instead of flipping them to false - #270
Merged
Merged
Conversation
JingMatrix
force-pushed
the
fix/persist-rkp-only-knobs
branch
from
October 2, 2026 10:23
d023eaa to
4322dc9
Compare
Owner
Author
|
@waichanrx @Retro2512 @FroggMaster @bocho8 @KoljaFrahm @rossiscool123 — this is my final design here: instead of flipping the Test build: https://github.com/JingMatrix/TEESimulator/actions/runs/36995223456 (sign in, grab the Debug or Release artifact at the bottom). Please confirm: on the Keyboxes screen, delete the two RKP-only knobs, reboot, and check they stay gone — and that Restore puts them back. Thanks! |
JingMatrix
force-pushed
the
fix/persist-rkp-only-knobs
branch
from
October 19, 2026 09:52
4322dc9 to
8d0a4ac
Compare
The two remote_provisioning.*.rkp_only properties gate whether keystore2 provisions attestation keys only through RKP or may fall back to a batch key; letting the batch key through is what routes a target app's key generation onto our keybox. They are plain system properties, not persist.*, and a vendor .prop that init re-applies each boot can reassert the on default (#236), so a live resetprop reverts on the next boot (#244) -- and on some devices a delayed config sync reasserts it hours later with no reboot at all (#262). Writing false also leaves an anomalous value behind: a device that ships true now reads false, which is itself a signal. So instead of flipping the property, the user now deletes it. resetprop --delete removes it entirely; keystore2 and our hook both read an unset property as false, so the keybox path is reached exactly as before, but the device now looks like one that never declared RKP-only enforcement rather than one with a suspiciously flipped value. The choice is recorded as a delete intent in /data/adb/teesim/rkp.json ({ property: value-at-deletion }) and re-enforced at daemon start and on every push (applyRkpDeletions): a property the vendor .prop brought back is simply deleted again. Only the two known rkp_only names are ever deleted or persisted, so a stale or hand-edited rkp.json can never steer resetprop at an arbitrary property. Because a deleted property reads back as unset -- indistinguishable from one the device never shipped -- the daemon exposes its delete-intent list (GET /rkp) so the WebUI can tell the two apart: a present property shows a trash control that deletes it, a deleted one shows a Restore control (carrying the value remembered at deletion) that sets it back and clears the intent, and a property that is merely absent is hidden. Delete and restore run in the daemon under the same lock as the boot/push re-enforce (POST /rkp/delete, POST /rkp/restore), so a live change can never interleave with it. Each of them also re-pushes the config, because the push is what carries the delete-intent set to the hook: the data-dir watcher only fires for config.json and keyboxes, so without it a fresh intent would not reach the hook until some unrelated push happened, and until then a reasserted property would read as a genuine rkp_only=true. Restoring a property that was already unset when it was deleted only drops the intent -- writing a plausible-looking true onto a device that never declared the knob would leave exactly the anomalous reading deletion exists to avoid. enable_rkpd is persist.device_config and already survives on its own, so it is no longer routed through the daemon or persisted here -- it stays a plain WebUI toggle. Some devices reassert the property hours after boot, between pushes, with no reboot at all (#262), which the eager pass alone would miss. The daemon pushes the delete-intent set down to the hook; when the hook reads an rkp_only prop live despite that intent, it still allows that one lookup -- denying an rkp-only level fails key generation outright, worse than the single check it races with -- but notes the reassertion, which rides back on the getUsage reply. The daemon then re-deletes the property live, so the next provisioning reads it unset and takes the keybox path. keystore1 never resolves a remotely-provisioned key, so it no-ops both ends. Also fixes the NDK that rust/build.sh resolves, which the clippy step now trips over. It took the newest NDK under $ANDROID_HOME/ndk, and on a CI runner that is a preinstalled one rather than the version the workflow installs; NDK 30 rejects the unversioned target triple cargo-ndk passes, so openssl-sys's bindgen dies on its cdefs.h. It now honours ANDROID_NDK_HOME and ANDROID_NDK_ROOT first, which is how the workflow already passes the pin. CMake always passed NDK_HOME explicitly, so only a direct build.sh call was ever affected. Closes #244.
JingMatrix
force-pushed
the
fix/persist-rkp-only-knobs
branch
from
October 19, 2026 11:01
8d0a4ac to
e0fd8df
Compare
Draft
6 tasks
Zhanfg
added a commit
to Zhanfg/TEESimulator
that referenced
this pull request
Oct 22, 2026
…ingMatrix#270) Stage safe patch hunks from f3fbd12 onto isolated OnePlus integration candidate. Vendor-overlapping paths require separate semantic reconciliation before CI.
Zhanfg
added a commit
to Zhanfg/TEESimulator
that referenced
this pull request
Oct 22, 2026
… architecture Port App, KeyAdmin and C ABI changes from upstream JingMatrix#270 while preserving OnePlus conformance routes, startup protections and documented endpoints.
Zhanfg
added a commit
to Zhanfg/TEESimulator
that referenced
this pull request
Oct 22, 2026
…dware-aware fork Integrate upstream JingMatrix#270 into compatibility RKP policy without altering strict hardware routing. Preserve OEM read-only RKP indicator in the WebUI.
This was referenced Oct 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The two
remote_provisioning.*.rkp_onlyproperties gate whether keystore2 provisions attestation keys only through RKP or may fall back to a batch key; letting the batch key through is what routes a target app's key generation onto our keybox. They are plain system properties, notpersist.*, and a vendor.propthat init re-applies each boot can reassert theondefault (#236), so a liveresetpropreverts on the next boot (#244) — and on some devices a delayed config sync reasserts it hours later with no reboot at all (#262). Flipping the value tofalsealso leaves an anomalous reading behind: a device that shipstruenow reportsfalse.So instead of flipping the property, the Keyboxes screen now deletes it.
resetprop --deleteremoves it entirely; keystore2 and our hook both read an unset property asfalse, so the keybox path is reached exactly as before, but the device looks like one that never declared RKP-only enforcement rather than one with a suspiciously flipped value. The choice is recorded as a delete intent in/data/adb/teesim/rkp.json({ property: value-at-deletion }) and re-enforced at daemon start and on every push: a property the vendor.propbrought back is simply deleted again. Only the two knownrkp_onlynames are ever deleted or persisted, so a stale or hand-editedrkp.jsoncan never steerresetpropat an arbitrary property.A deleted property reads back as unset — indistinguishable from one the device never shipped — so the daemon exposes its delete-intent list (
GET /rkp) and the WebUI tells the two apart: a present property shows a trash button that deletes it, a deleted one shows a Restore control carrying the value remembered at deletion, and a property that is merely absent is hidden. Delete and restore run in the daemon under the same lock as the boot/push re-enforce (POST /rkp/delete,POST /rkp/restore), so a live change can never interleave with it.Each of them also re-pushes the config, because the push is what carries the delete-intent set to the hook: the data-dir watcher only fires for
config.jsonand keyboxes, so without it a fresh intent would not reach the hook until some unrelated push happened, and until then a reasserted property would read as a genuinerkp_only=trueand never be reported for a re-delete. Restoring a property that was already unset when it was deleted only drops the intent — writing a plausible-lookingtrueonto a device that never declared the knob would leave exactly the anomalous reading deletion exists to avoid.enable_rkpdispersist.device_configand already survives on its own, so it stays a plain toggle and is not persisted here.Some devices reassert the property hours after boot, between pushes, with no reboot at all (#262), which the boot/push pass alone would miss. The daemon pushes the delete-intent set down to the hook; when the hook reads an
rkp_onlyproperty live despite that intent, it still allows that one lookup — denying an rkp-only level fails key generation outright, worse than the single check it races with — but notes the reassertion, which rides back on thegetUsagereply. The daemon then re-deletes the property live, so the next provisioning reads it unset and takes the keybox path. keystore1 never resolves a remotely-provisioned key, so it no-ops both ends.Also folded in a CI fix the clippy step now trips over:
rust/build.shresolved the newest NDK under$ANDROID_HOME/ndk, and on a runner that is a preinstalled one rather than the version the workflow installs. NDK 30 rejects the unversioned target triple cargo-ndk passes, soopenssl-sys's bindgen dies on itscdefs.h(Unversioned target triples are not supported!). It now honoursANDROID_NDK_HOME/ANDROID_NDK_ROOTfirst, which is how the workflow already passes the pin; CMake always passedNDK_HOMEexplicitly, so only a directbuild.sh clippycall was affected.Closes #244.