I'm implementing Apple's App Attest service in a Swift iOS app using DCAppAttestService, with a TypeScript backend hosted on Cloudflare Workers.
Key generation and attestation registration both work correctly. The server successfully validates Apple's certificate chain, confirms that the public key matches the key ID, and stores the P-256 public key.
However, when the app calls generateAssertion, the server consistently fails to verify the returned signature.
Here's what I've checked so far:
- The app passes SHA256(clientData) to generateAssertion, as Apple's documentation requires.
- The server constructs the verification message using authenticatorData || SHA256(clientData).
- I've confirmed that the client and server use matching hashes, assertion data, authenticator data, and verification messages.
- The stored public key matches the App Attest key ID.
- The returned signature has a valid DER structure.
- Signature verification fails using both Web Crypto (with DER-to-P1363 conversion) and a separate Node.js verification method.
- Both verification methods work correctly with independently generated test signatures.
- Challenge validation, RP ID checks, environment checks, and extension validation all pass.
One thing that stands out:
The assertion's authenticator data contains the flags 0xC0 (AT + ED). An extension data map appears immediately after the standard 37-byte header, without the credential data that would normally be expected when the AT flag is set.
Apple mentioned new App Attest assertion extensions in WWDC26, but I haven't found documentation explaining whether this specific format is expected on iOS 27.
My server includes the entire original authenticator data, including the flags and extensions, when verifying the signature. Nothing is removed or modified.
I have three questions:
-
Is 0xC0 a valid flag combination for App Attest assertions on iOS 27 when extension data follows the 37-byte header without a separate credential data section?
-
Does the standard signature verification formula, SHA256(authenticatorData || SHA256(clientData)), still apply when these new extensions are present?
-
Are there any known issues or changes in iOS 27 that could cause generateAssertion to succeed while the server cannot verify its signature using the public key obtained during registration?
I'm testing on a physical iPhone using a Debug development build, not the simulator. I've also kept the original App Attest key and registration unchanged throughout testing.
At this point, the client and server appear to agree on all the data used for verification, but the signature still fails. I'm trying to determine whether I'm missing something about the new assertion format or whether there is another part of the verification process I should investigate.
Any guidance would be appreciated.