Unfit Key Material Object
URI: https://docs.ankatech.co/errors/unfit-key-material-object
HTTP Status: 409 Conflict
When you see this
Provisioning a tenant's key-encryption key looks for an object already present under that tenant's deterministic KEK label and adopts it instead of creating a second key beside it. This refusal says the object found under that label is not a key this platform creates, so it was not adopted.
The platform creates exactly one shape of key-encryption key: AES, 32 bytes (AES-256), CKA_SENSITIVE=true, CKA_EXTRACTABLE=false. Adoption admits exactly that shape, positively confirmed — an attribute the token declines to report fails in the same way a wrong value does, and an object whose handle no longer resolves fails too.
Nothing was created and nothing was adopted. The object is examined before any key is generated, so the token is left exactly as it was found.
Why this exists
Each tenant's label is deterministic, because that is what makes adoption idempotent. Deterministic also means plantable: anyone holding the token's crypto-user credential could place an extractable AES key under a tenant's label before the platform provisions it. Adopted, that key would become the tenant's KEK, and every data key wrapped under it would be readable by whoever can export it.
Adoption therefore checks what it adopts, on both planes that adopt:
- key material (Plane 3) — the tenant KEK on the deployment token, and on an ANKA-managed Edition tier's token;
- deployment-secret custody (Plane 2) — the per-tenant secret-custody KEK reused when a tenant-scoped secret is sealed.
Common Causes
- An object was placed on the token under a tenant's KEK label by something other than this platform — out-of-band tooling, a restored or cloned partition, or a migration script.
- A key exists under the label but is extractable, is not sensitive, is not AES, or is not 256 bits.
- The token will not report
CKA_KEY_TYPE,CKA_VALUE_LEN,CKA_SENSITIVEorCKA_EXTRACTABLEfor the object. An unanswerable attribute is treated exactly as a wrong one: the platform cannot confirm the shape, so it does not adopt.
Response Example
{
"type": "https://docs.ankatech.co/errors/unfit-key-material-object",
"title": "Unfit Key Material Object",
"status": 409,
"detail": "The key-material token holds an object under this tenant's key-encryption-key label that is not confirmed to be a sensitive, non-extractable AES-256 key, which is the only kind of key-encryption key this platform creates. Unconfirmed: CKA_SENSITIVE, CKA_EXTRACTABLE. Nothing was adopted and nothing was created: adopting it could make a key that is readable outside the token this tenant's key-encryption key. Remove the object from the security domain out of band.",
"instance": "/api/v3/admin/tenants/3fa85f64-5717-4562-b3fc-2c963f66afa6/keys",
"extensions": {
"requestId": "d6194e72-3f0b-4a85-81c9-4b70e2ad5163"
}
}
Content-Type: application/problem+json — the response follows RFC 9457 Problem Details.
The detail names the unconfirmed attributes and nothing else. Unconfirmed: lists which of the four attributes the token did not confirm — CKA_KEY_TYPE (not AES), CKA_VALUE_LEN (not 32 bytes), CKA_SENSITIVE (not true), CKA_EXTRACTABLE (not false) — or, when the label resolved a moment ago and names nothing now, the object itself, which no longer resolves under the label. It carries an attribute name, never its value, and never the object's label, because the body travels to the console, the SDK and whatever logs the client keeps. Naming the attribute discloses nothing to a party who planted the object — it holds the crypto-user credential and can read its own object — while it is exactly what an operator diagnosing a legitimately mis-created key needs. No slot, no token serial and no PKCS#11 return code appears in it either.
How to Resolve
The remedy is out of band and on the token. Re-running the operation returns the same refusal until the object is gone.
- Identify the tenant and the token. The body names neither the object nor its label. The tenant is the one in
instance, and this deployment's own log names it too. Which token: the on-demand key operations and tenant creation address the deployment token; the Edition assign addresses the target tier's token; sealing a tenant-scoped secret addresses the secret-custody token. - Locate the object that tenant's label resolves to, following the operator recovery procedure for key-protection backends, or with ANKATech support.
- Confirm it is not a key you need. It is not one this platform created — every KEK this platform creates is a sensitive, non-extractable AES-256 key, and
detailnames which of those properties the object failed — but it may hold material for something else on the same security domain. - Remove it with the vendor's own tooling, then retry the operation.
- If the object cannot be removed, bind this deployment to a security domain of its own — a separate partition, Security World or cluster — rather than sharing one with whatever placed it there.
Why
409and not503. The token answered. Its state contradicts the operation and stays that way until an operator acts, so the same call a minute later fails identically. A503would be a retry instruction for a condition that cannot clear itself. This takes the same classification as the ambiguous-coordinate refusal, for the same reason.
Where it is raised
Raised by the PKCS#11 key-encryption-key provisioner on any operation that creates or adopts a tenant KEK, and by the deployment-secret custody provisioner when a tenant-scoped secret is sealed. The five Cloud KMS families adopt no PKCS#11 object and this refusal cannot occur on them.
Two envelopes, and the operation you called does not decide which one you get — the same split the foreign-environment claim has, for the same reason.
-
As this typed
409, on a deployment that declares no key-backend descriptor:POST /api/v3/admin/platform/tenants/provision— the eager key-encryption-key step of tenant creation, on a host-bound PKCS#11 token.POST /api/v3/admin/tenants/{tenantId}/keys, the enrolment of an authenticator app (TOTP,createTotpEnrolmentor the sign-in transaction'stotp-enrolmentstep) and the platform's internal tenant-resolution call (its create branch) — the operations that provision a tenant KEK on demand.
-
As this typed
409regardless of whether a descriptor is declared, because neither path goes through the deployment descriptor provisioner:PUT /api/v3/admin/platform/tenants/{tenantId}/key-backend/edition— assigning an Edition establishes the tenant's own KEK on that tier's carrier, so an unfit object under the tenant's label on that tier's token is refused there, with no Edition assigned and no custody row written.- Any operation that seals a tenant-scoped secret, which provisions or reuses that tenant's secret-custody key-encryption key on the secret-custody token.
-
As a
422, onPOST /api/v3/admin/platform/setup/backend/activate(and the hostinit-hsm.sh <env> --activatethat delegates to it) — and, on a deployment that declares a PKCS#11 descriptor, on the on-demand operations above as well. There the deployment provisioner catches the condition and re-presents it asbackend-kek-provisioning-failedcarryingextensions.reason: REFUSED— REFUSED and not UNREACHABLE, because the token answered and the remedy is on it rather than in this deployment's networking or credentials. That envelope'sdetailis fixed and names no coordinate and no attribute; this deployment's own log names the tenant the refused object belongs to, which is where step 1 above starts.
Related
- Backend Key Provisioning Failed — the
422this condition wears on the activation path - Foreign Environment Claim — the token is another environment's, or holds unclaimed bootstrap KEKs. A different contradiction on the same adoption path
ambiguous-key-material-object— more than one object carries the tenant's KEK label, so nothing can decide between them. The sibling refusal: this page is the one-object case- Key Protection Backend* — how tenant KEKs are provisioned and adopted