Issue a fresh per-request challenge from your server and send it to the iOS client before each sensitive request
On device, compute clientData (challenge concatenated with request-specific bytes), compute SHA256(clientData), and call DCAppAttestService.generateAssertion(keyIdentifier, clientDataHash:)
Send the assertion object (Base64-encoded), the raw clientData, and the request payload to your server
On the server, decode the assertion CBOR; concatenate authenticatorData with SHA256(clientData) and compute SHA256 of the result to form the nonce
Verify the ECDSA signature in the assertion against the nonce using the stored public key from the initial attestation step
Check that the assertion's authenticatorData counter is strictly greater than the previously stored counter value; update the stored counter on success
Known gotchas
The assertion counter is monotonically increasing per key; a replayed assertion will have a counter equal to or less than the stored value, which must be treated as a hard rejection, not a soft warning
Per-request challenges must be single-use and time-bounded; a static or reused challenge allows assertion replay attacks even with correct signature verification
On the client side, DCAppAttestService.generateAssertion returns an error if the key has never been attested; always guard assertion calls with a prior successful attestation flow
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp