TigerTrust’s attestation subsystem provides cryptographically-gated certificate issuance for TPM-equipped devices. The flow is a classic quote-and-certify exchange:
  1. The device requests a single-use nonce from /api/iot/devices/:id/attestation/challenge.
  2. The device produces a TPM2_Quote over selected PCRs (with the nonce as qualifying data) and a TPM2_Certify binding the CSR public key to a TPM-resident Attestation Key.
  3. The device submits the full evidence bundle to /api/iot/devices/:id/attest-and-provision, which consumes the nonce, evaluates the matching attestation_policy, calls PKI Core’s /certificates/sign-attested verifier, and returns a certificate on success or 403 attestation_failed with structured failures.
Attestation endpoints return raw JSON without the { data: ... } envelope used by the rest of the backend. Errors are { "error": "code", "details": ... }.

Attestation policies

Policies are the trust anchors — they declare which PCR values, PCR banks, EK manufacturers, and freshness constraints are acceptable for a given device type.

List policies

GET /api/attestation/policies
deviceType
string
Filter to policies scoped to a given device type. Omit for workspace-wide list.
[].id
integer
[].name
string
[].description
string
[].deviceType
string
Matches iot_devices.deviceType. null = matches any type.
[].expectedPcrs
object
Nested map: { "SHA256": { "0": "hex", "7": "hex", ... } }.
[].requiredPcrBanks
string[]
Default ["SHA256"].
[].requiredPcrs
integer[]
Default [0,1,2,3,4,7].
[].allowedEkManufacturers
string[]
e.g. ["Intel","Infineon","STMicro"]. Empty = any TPM vendor trusted root.
[].maxAttestationAgeSeconds
integer
default:"300"
[].requireBoundCsrKey
boolean
default:"true"
Enforces TPM2_Certify.
[].requireSecureBoot
boolean
default:"true"
[].enabled
boolean

Get a policy

GET /api/attestation/policies/:id Returns 404 Policy not found if the ID doesn’t exist.

Create a policy

POST /api/attestation/policies Body is validated against insertAttestationPolicySchema from @shared/schema. Returns 400 { "error": "Invalid policy", "details": {...} } on validation failure.
name
string
required
deviceType
string
expectedPcrs
object
required
requiredPcrBanks
string[]
requiredPcrs
integer[]
allowedEkManufacturers
string[]
maxAttestationAgeSeconds
integer
requireBoundCsrKey
boolean
requireSecureBoot
boolean
enabled
boolean
default:"true"
curl -X POST https://api.tigertrust.example.com/api/attestation/policies \
  -H "X-API-Key: ck_9f2a...7c4e" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "EdgeRouter v3 golden image",
    "deviceType": "edge_router",
    "expectedPcrs": {
      "SHA256": {
        "0": "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969",
        "7": "9dc7dd21c1893a7f88bc0f7c2eb2b8a4b4d3d47b3f1a6c4e0f2b8a9d7c5e3f1b"
      }
    },
    "requiredPcrs": [0, 1, 4, 7],
    "allowedEkManufacturers": ["Infineon"],
    "requireBoundCsrKey": true,
    "requireSecureBoot": true
  }'

Update a policy

PATCH /api/attestation/policies/:id — partial update.

Delete a policy

DELETE /api/attestation/policies/:id — returns 204 No Content.

Nonce challenge

POST /api/iot/devices/:id/attestation/challenge Mints a single-use random nonce (32 bytes hex-encoded) bound to a device. The nonce must be included in the subsequent TPM2_Quote as qualifyingData.
id
integer
required
Device ID.
ttlSeconds
integer
default:"300"
How long the nonce is valid.
purpose
'attestation' | 'ak_enrollment'
default:"attestation"
curl -X POST https://api.tigertrust.example.com/api/iot/devices/47/attestation/challenge \
  -H "X-API-Key: ck_9f2a...7c4e" \
  -H "Content-Type: application/json" \
  -d '{"ttlSeconds": 120, "purpose": "attestation"}'
{
  "nonce": "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969",
  "expiresAt": "2026-08-25T14:24:03.812Z",
  "purpose": "attestation"
}

Attest and provision

POST /api/iot/devices/:id/attest-and-provision The load-bearing gated-issuance endpoint. Consumes the nonce, resolves the matching policy (explicit policyId wins, otherwise the first enabled policy for the device’s type), and hands the evidence bundle to PKI Core’s verifier. On success the issued certificate is returned and a passing attestation is recorded. On verifier failure the endpoint returns 403 with structured failure reasons and stores a failing attestation with trustScore=0.
id
integer
required
caId
integer | string
required
Target CA (typically the internal device-issuing intermediate).
templateId
string
required
PKI Core template, e.g. iot_device.
csr
string
required
PEM-encoded CSR.
validityDays
integer
policyId
integer
Force evaluation against a specific policy. Otherwise the first enabled policy matching device.deviceType is used.
evidence
object
required
{
  "message": "Attested certificate issued",
  "certificate": {
    "certificate": "-----BEGIN CERTIFICATE-----\nMIID...\n-----END CERTIFICATE-----",
    "chain": [ "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" ],
    "serialNumber": "0A:1B:2C:3D",
    "attestation": {
      "policyId": 8,
      "verifiedPcrs": { "SHA256": { "0": "3d458cfe...", "7": "9dc7dd21..." } },
      "ekManufacturer": "Infineon"
    }
  }
}
{
  "error": "attestation_failed",
  "failures": [
    "pcr7_mismatch: expected 9dc7dd21... got 7f88bc0f...",
    "secure_boot_required_but_disabled"
  ]
}
Common 400 failure modes: nonce_not_found, nonce_already_used, nonce_expired, nonce_device_mismatch, or missing required fields (caId, templateId, csr, evidence.nonce and evidence.quoteBlob are required).

Non-attested device attestation records

For workflows that only want to record an attestation outcome without gating issuance:
  • GET /api/iot/devices/:id/attestations — history for a device
  • POST /api/iot/devices/:id/attest — record an arbitrary attestation
  • POST /api/iot/devices/:id/attest-tpm — record a TPM quote (no verifier gating)

See also