Home · docs · provider attestation
Provider attestation
Every production provider registration is backed by a chain that proves the physical Apple device and Secure Enclave key are genuine, binds the prompt-encryption key to that device, and rejects stale or mismatched registration frames. An APNs app-targeted nonce challenge is live and elevates admitted genuine apps to code_attested.
Unfamiliar terms (Secure Enclave, MDA, X.509)? See the glossary.
The five layers
Answer: Umbra attestation stacks six checks: a non-exportable Secure Enclave signing key, managed-device identity, Apple MDA X.509 chain validation, five-minute signed-blob freshness, binding of the prompt encryption key to that signed blob, and an APNs app-targeted nonce challenge for running-code identity.
Secure Enclave P-256
The private key is generated and held inside the SE; it never leaves the chip, even to RAM. The provider signs the canonical attestation blob with this key. The coordinator verifies the signature against the public key in the blob.
MDM / managed identity
The real production path uses Managed Device Attestation material for the managed Secure Enclave identity. Caller-supplied device inventory is not trusted for routing, pay, or UI proof.
Apple MDA X.509 chain
The coordinator receives Managed Device Attestation evidence through the MDM bridge, validates it against the Apple Enterprise Attestation Root CA, then cross-checks serial and freshness/key binding against the signed register blob.
Signed-blob freshness
The signed blob carries a timestamp. During provider registration, the coordinator rejects blobs older than five minutes and rejects any encryption key that differs from the signed blob.
APNs running-code identity
The signed/profile-bound app receives an app-targeted APNs nonce challenge and returns the matching proof. Successful challenges elevate an MDA-verified provider from hardware to code_attested; authenticated private routing fails closed below that tier.
On a real Mac
Answer: Production providers run the signed Umbra Provider.app, which creates or reloads a profile-bound Secure Enclave P-256 key, signs the attestation blob, and lets the coordinator match that key against fresh MDM evidence.
The production path is the Swift binary inside Umbra Provider.app. It calls, in-process:
// Generate the SE keypair (non-exportable, hardware-backed)
let priv = SecKeyCreateRandomKey([
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits: 256,
kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
], &error) as! SecKey
// Submit the signed blob and prompt-encryption key in the WebSocket register frame
try await coordinator.register(
attestation: attestation, // blob + DER signature
sePublicKey: priv.publicKeyDER(),
encryptionKey: x25519.publicKey
)From a shell:
# First review https://tryumbra.dev/download/ and its system-change disclosure.
curl -fsSL https://tryumbra.dev/install.sh | sh
# The first install starts guided setup automatically.
umbra statusThe public DMG is Developer ID-signed, notarized by Apple, and published with a separate SHA-256 sidecar. The download page documents every local change and the complete removal flow before installation.
Development mode
Developers can still run local/synthetic tests from the private repo, but those paths are explicitly insecure and must not receive public prompts or accrue payable earnings.
The registration freshness protocol
provider coordinator
| |
| SE_Sign(canonical({ |
| publicKey: se_pub, |
| encryptionPublicKey: x25519_pub, |
| serialNumber, |
| timestamp |
| })) |
| |
| WebSocket first frame: register |
| { blob, signature, x25519_pub } |
| |
| verify: signature, 5-min timestamp, |
| MDM/MDA evidence, serial, |
| freshness + X25519 binding |
| |
| <- registered ok or register_error |If the signature is invalid, the timestamp is stale, MDM evidence is missing, or the prompt-encryption key does not match the signed blob, the coordinator rejects the registration and closes the socket.
Coordinator verification (Go)
Answer: The Go verifier checks the SE signature, five-minute freshness, coordinator-held MDM/MDA evidence, serial binding, and the signed X25519 prompt key. Any failure rejects registration.
The full verifier is in coordinator/internal/verifier/ and the MDM binding code is in coordinator/internal/mdmtrust/:
- Verify the Secure Enclave signature over the canonical JSON blob.
- Load fresh MDM evidence for the same device and provider key hash.
- Verify the Apple Enterprise Attestation chain and freshness nonce in that evidence.
- Cross-check serial, freshness binding, and the signed X25519 prompt-encryption key.
All checks fail-closed. Test vectors live in coordinator/internal/verifier/, coordinator/internal/mdmtrust/, and provider/Tests/UmbraProviderTests/.
umbra/docs/attestation.md in the repo. The umbra/provider/ Swift code is the production implementation; the Go verifier and MDM binding code are the load-bearing checks.Frequently asked questions
What is Umbra provider attestation?
Umbra providers sign a WebSocket register frame with a Secure Enclave key and present Apple Managed Device Attestation (MDA) evidence so the coordinator can verify the Mac is genuine Apple Silicon and bind prompt encryption to that device.
What trust tier do production Umbra providers reach today?
Real admitted Mac providers reach code_attested after Secure Enclave identity, Apple MDA chain validation, five-minute signed-blob freshness, serial and X25519 prompt-key binding, plus an APNs app-targeted nonce challenge that proves the genuine provider app can receive and answer the challenge.
How does the coordinator verify a provider register frame?
The coordinator checks the SE signature on the canonical blob, validates the Apple MDA certificate chain, enforces a five-minute freshness window, matches the device serial, and rejects any encryption public key that differs from the signed blob.
Can a Mac owner read buyer prompts if attestation passes?
The hardware tier is designed so the operator cannot extract the Secure Enclave key or attach a debugger to the hardened in-process inference binary. Prompts are decrypted only inside that process and are not written to disk.
Where is attestation implemented in the Umbra codebase?
The Swift provider at umbra/provider/Sources/UmbraProvider/ generates the SE key and signs the register blob. The Go coordinator validates the signature plus coordinator-held MDM/MDA evidence before admitting a provider.