Apple and Android Attestation Formats
Once you accept direct attestation from platform authenticators, the formats you meet are no longer packed. This page covers the Apple anonymous format, Android key attestation and the legacy SafetyNet variant — what each proves, how each is verified, and why none of them gives you the device identity that teams often expect. It sits under Attestation vs Assertion Explained; the FIDO2 default format is covered in understanding packed attestation.
Format Reference
| Format | Chains to | Carries a signature field | Model identity |
|---|---|---|---|
apple |
an Apple root | no — the binding is a nonce in the certificate | none by design |
android-key |
a Google root | yes | keystore properties, not a model |
android-safetynet |
a Google root, via a signed token | inside the token | legacy; being retired |
none |
— | — | none |
The absence of a signature field in the Apple format is the detail that most often breaks a verifier written against packed: there is nothing to verify in the usual place, because the binding lives in a certificate extension instead.
Root Cause Analysis
1. Expecting a signature field. An Apple statement has no sig. A verifier that requires one rejects every Apple registration, and the error message will point at a missing field rather than at the wrong assumption.
2. Skipping the nonce comparison. The Apple format binds a certificate to a ceremony by embedding a nonce derived from authenticatorData and the client-data hash. Without recomputing and comparing that nonce, a certificate from any ceremony would satisfy the check.
3. Treating Android key attestation as universally available. Its contents vary by vendor, by Android version and by whether the key was generated in the secure element. Fields you rely on may simply be absent on hardware you have not tested.
4. Building on SafetyNet. It is a legacy path that is being retired in favour of newer platform mechanisms. Code that requires it will break; code that accepts it as one of several formats will not.
5. Expecting a device identifier. None of these formats provides one. Apple’s is explicitly anonymous, and Android’s describes key protection rather than device identity.
Step-by-Step Resolution
Step 1 — dispatch on fmt and accept the unknown
Route each format to its own verifier and route everything unrecognised to your documented no-device-claim path. New formats appear; a dispatch table with a hard failure at the end will meet one eventually.
Step 2 — implement the Apple path around the nonce
Compute SHA-256 over authenticatorData concatenated with the client-data hash, extract the nonce from the certificate extension, and compare byte-for-byte. Then validate the chain to a pinned Apple root.
Step 3 — implement the Android path around the keystore extension
Verify the statement signature, validate the chain to a pinned Google root, then read whichever keystore properties your policy consumes — treating every field as optional.
Step 4 — treat SafetyNet as deprecated but accepted
Verify it if you receive it; do not build policy that requires it.
Step 5 — record the format alongside the outcome
Which format a credential arrived with is a useful population statistic and the fastest way to notice a platform behaviour change.
Verification and Testing
One captured registration per format, with its trust anchor, is the only fixture set that will keep this code honest. Synthetic statements test your verifier against your own assumptions rather than against the platforms.
For the Apple path, add a fixture whose nonce does not match the ceremony and assert it is rejected. That single test is what proves the binding is actually checked rather than merely parsed.
For the Android path, add a fixture missing an optional keystore field and assert that verification still succeeds while the policy branch handles the absence. Optional fields being treated as mandatory is the most common source of vendor-specific failures.
Add an unknown-format fixture and assert it reaches your no-device-claim path without throwing.
Pitfalls
1. One verifier for all formats. They share almost nothing; a shared code path hides at least one wrong assumption.
2. Requiring keystore properties that are optional. Produces failures confined to particular vendors and Android versions.
3. Assuming platform attestation identifies a device. It does not, and designing around the expectation that it might wastes effort.
Frequently Asked Questions
Will I receive attestation from a synced passkey at all?
Frequently not. Platform authenticators increasingly return no attestation statement regardless of what was requested, because a synced credential exists on several devices and there is no single device to attest to. Code that treats an absent statement as an error rejects a large and growing share of legitimate registrations.
Does Apple’s format let me distinguish one Mac from another?
No. It is anonymous by design: the statement proves the credential was created by genuine Apple hardware and nothing more. Device-level identity is deliberately withheld, and there is no configuration that changes this for a web relying party.
Is Android key attestation worth implementing?
Only if a policy consumes its keystore properties — for instance, requiring that the key is hardware-backed and that user authentication is enforced by the keystore. For a consumer product with no such requirement it is verification work with no consumer.
What replaces SafetyNet?
Newer platform integrity mechanisms, with the practical effect for a relying party being that fewer registrations carry that format over time. The right posture is to keep accepting it while it appears and to build nothing that depends on it.
Should the format influence what the credential can do?
Only if you have a written requirement it maps to. In most products the sensible design is that every verified registration produces an equally usable credential, and attestation influences only which credentials satisfy a higher-assurance policy for particular actions.
How do I obtain the platform roots?
From the platform vendors’ published certificate material, pinned in your deployment. As with any attestation root, retrieving it over the same channel as the statement it authenticates provides no assurance.
Do these formats appear in enterprise deployments differently?
They can. A managed device may produce richer attestation than the same hardware would outside management, because platform policy governs what is disclosed. That is worth knowing when a behaviour differs between a test device and a corporate one.
How should platform attestation change what a credential is allowed to do?
In most products, not at all. The credential is equally usable for sign-in whether or not a statement arrived, and the only defensible use of the format is as one input to a policy that gates something more sensitive than reading an account. Where a genuine requirement exists — a regulated workflow that must run on hardware-backed keys, for instance — express it as a tier rather than as an enrolment barrier: accept the credential, record what it did and did not prove, and let the higher-assurance surfaces ask for something stronger. That keeps enrolment open for everyone while giving the policy a real place to apply, and it avoids the failure mode where a platform behaviour change quietly stops a whole population from being able to register.
Recording the format alongside every registration also gives you a cheap early warning when a platform changes its behaviour, since a sudden shift in the distribution is visible long before anybody files a report about it.
Related
- Attestation vs Assertion Explained — the parent topic area
- Understanding Packed Attestation Format — the FIDO2 default format
- Authenticator Metadata and MDS3 — where model facts come from when a format supplies an AAGUID
- Attestation Conveyance Policy Selection Guide — deciding whether to request any of this