Skip to content

Sign a new attest key under the caller's attest key instead of the keybox - #263

Merged
JingMatrix merged 2 commits into
devfrom
attest-key-graph
Aug 20, 2026
Merged

Sign a new attest key under the caller's attest key instead of the keybox#263
JingMatrix merged 2 commits into
devfrom
attest-key-graph

Conversation

@JingMatrix

Copy link
Copy Markdown
Owner

generateKey mints every ATTEST_KEY-purpose key in the TA and, until now, always self-attested it under the keybox, discarding any attestation key keystore2 injected with the request. That is right for a bare attestation key -- keystore2 injects an RKP-provisioned hardware key we cannot re-root, so rooting the new key at the keybox is the only option -- but wrong once the caller has built a key graph with setAttestKeyAlias. There the caller names an attest key A and expects the new key B's leaf to be signed by A, so that verifying B against A's public key succeeds. When A is one of ours, self-attesting B under the keybox breaks the graph: B's leaf carries A's subject as its issuer but a keybox signature, and the chain the caller assembled verifies under neither A nor the keybox.

The attest-key branch now checks whether the injected attestation key is ours and, if so, signs B's leaf with it, keeping the A -> B relationship intact; it falls back to keybox self-attestation only when no key was injected or the injected one is a foreign RKP key we cannot re-root. The ordinary business-key path already did this for leaves that are not themselves attest keys -- this extends the same rule to the attest-key case.

…ybox

`generateKey` mints every `ATTEST_KEY`-purpose key in the TA and, until now, always self-attested it under the keybox, discarding any attestation key keystore2 injected with the request. That is right for a bare attestation key -- keystore2 injects an RKP-provisioned hardware key we cannot re-root, so rooting the new key at the keybox is the only option -- but wrong once the caller has built a key graph with [`setAttestKeyAlias`](<https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder#setAttestKeyAlias(java.lang.String)>). There the caller names an attest key A and expects the new key B's leaf to be signed by A, so that verifying B against A's public key succeeds. When A is one of ours, self-attesting B under the keybox breaks the graph: B's leaf carries A's subject as its issuer but a keybox signature, and the chain the caller assembled verifies under neither A nor the keybox.

The attest-key branch now checks whether the injected attestation key is ours and, if so, signs B's leaf with it, keeping the A -> B relationship intact; it falls back to keybox self-attestation only when no key was injected or the injected one is a foreign RKP key we cannot re-root. The ordinary business-key path already did this for leaves that are not themselves attest keys -- this extends the same rule to the attest-key case.
The header above the ATTEST_KEY block still read as the old absolute behaviour --
"always IGNORES any injected attest key" and "Forward std::nullopt: the TA
self-attests under the keybox" -- which the ours-key branch just below now
contradicts on its first line. Reword it to state the split (mint in the TA; use
the injected key only when it is ours, else self-attest under the keybox) and let
the two inner comments carry the detail. The forced-generation log line likewise
said "ignoring any injected attest key", true only in the fallback now, so it
reads "no usable injected attest key". Comment and log text only.
@JingMatrix
JingMatrix merged commit 700a1eb into dev Aug 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant