{
  "skill_name": "device-integrity",
  "evals": [
    {
      "id": 0,
      "name": "app-attest-attestation-verifier",
      "prompt": "Our backend team is writing the endpoint that receives App Attest attestation objects from the iOS client. List what the verifier has to check and what it should persist afterwards.",
      "expected_output": "Walks through CBOR decoding, certificate chain validation against the App Attest root CA, nonce = SHA256(authData || SHA256(challenge)) matched against the certificate value under OID 1.2.840.113635.100.8.2, public key hash equal to keyId, RP ID hash equal to SHA256(teamID.bundleID), counter 0, aaguid for the expected environment, credentialId equal to keyId, then storing the public key and receipt and consuming the challenge only after success.",
      "files": [],
      "expectations": [
        "Composes the nonce from authData and the hashed challenge, SHA256(authData || SHA256(challenge)), rather than hashing the challenge alone",
        "Compares the nonce with the certificate extension at 1.2.840.113635.100.8.2",
        "Checks the RP ID or App ID hash against team ID plus bundle ID",
        "Checks aaguid for the expected environment and an initial counter of 0",
        "Requires credentialId and the public key hash to match keyId",
        "Keeps the public key and receipt after verification, not the raw attestation object"
      ]
    },
    {
      "id": 1,
      "name": "gift-card-assertion-flow",
      "prompt": "We want to protect our 'redeem gift card' POST endpoint with App Attest assertions. Show the client side and explain what the server verifies.",
      "expected_output": "Fetches a fresh one-time challenge per request, builds client data with the challenge plus method, path and a body digest, hashes it, calls generateAssertion, sends assertion and client data in headers; server checks that the stored key signed SHA256(authenticatorData || SHA256(clientData)), RP ID, a strictly increasing counter, and that the challenge and request context match.",
      "files": [],
      "expectations": [
        "Uses a fresh server-issued one-time challenge inside the client data",
        "Binds request context or a body hash into the signed client data",
        "Describes the signature as covering SHA256(authenticatorData || SHA256(clientData))",
        "Server checks the RP ID hash and that the counter increases",
        "Server confirms the challenge was issued and not yet consumed"
      ]
    },
    {
      "id": 2,
      "name": "integrity-plan-review",
      "prompt": "Review our plan: cache the DeviceCheck token for a day, generate a new App Attest key each launch, retry every DCError with a new key, and use App Attest instead of OAuth sessions and certificate pinning. What would you change?",
      "expected_output": "Makes DCDevice tokens single-use, keeps one key per account per device and warns that extra keys affect risk metrics, retries serverUnavailable reusing its keyId and clientDataHash while discarding the key for other attestation errors, explains invalidKey causes (already attested, not attested, rejected) without inventing causes such as system updates or enclave resets, and routes OAuth, sessions and pinning to the authentication and swift-security skills.",
      "files": [],
      "expectations": [
        "Says DCDevice tokens are single-use and must not be cached",
        "Keeps one key per account per device and mentions the key-count risk metric",
        "On serverUnavailable, retries later reusing the identical keyId and clientDataHash",
        "Discards the key and generates a new one for other attestation errors",
        "Gives the three real invalidKey causes and does not invent others such as system updates or enclave resets",
        "Routes OAuth, sessions and pinning to sibling skills instead of App Attest"
      ]
    }
  ]
}
