Security Architecture Whitepaper
Version: Draft 1.0 · Scope: the security architecture, threat model and verifiability guarantees of the zero-knowledge Turkish digital identity verification platform.
1. Summary
VerifyBlind is a verification system that proves a person holds a real, live, NFC-chip-verified identity without disclosing their identity data to any integrating partner — or to the VerifyBlind infrastructure itself.
The principle at the heart of the design is this: sensitive identity data never leaves the path between the user's device and the AWS Nitro Enclave (a hardware-isolated, verifiable compute environment). The partner receives only the minimum verification result (e.g. age: 18+, a pseudonymous code).
This document describes the cryptographic design, the hardware root of trust (Nitro attestation + PCR0 version verification), NFC identity authentication, biometric liveness checking, session/ticket security, client integrity and build verifiability.
2. Design Principles
- Data minimisation / zero-knowledge: the partner learns not "who you are", but only whether the specific verification proposition it asked is true. Identity data is never transmitted to the partner.
- Isolation: all cryptographic decryption and identity-processing steps happen only inside the Nitro Enclave. The network-facing relay service (API) cannot see or store plaintext identity data.
- Verifiability: which enclave code is running is cryptographically proven by the PCR0 measurement in the attestation document; the integrity of the client apps is tested via platform attestation.
- No retention: VerifyBlind does not persistently store raw identity/biometric data; it keeps only the identity-non-revealing records needed for auditing (see §12).
3. System Architecture
Mobile App Relay (API) Enclave (Nitro)
(Android / iOS) :5102, network-facing isolated, /dev/nsm
─────────────── ───────────── ────────────────
NFC ID read ──TLS──► routing/ decryption,
liveness (face) rate limiting, NFC verification,
encryption (on device) nonce/state (Redis), biometrics, liveness,
audit/log ticket signing (MAC)
│
├── PostgreSQL 15
└── Redis 7 (nonce/state)
Partner Website ──► verifyblind.js widget ──► Relay (result channel)- Relay (ASP.NET 9): the only network-facing component. A bridge between mobile, partner and enclave; rate limiting, nonce/state management (Redis), audit logging and webhook delivery. It never sees plaintext identity data — the payload sent to the enclave is already encrypted on the device.
- Enclave (ASP.NET 9, ARM64/Graviton): runs inside an AWS Nitro Enclave. In production it is connected to the outside world only over vsock; its RAM is hardware-isolated from the host machine (parent EC2). All sensitive cryptography happens here.
- Data layer: PostgreSQL 15 (audit, consent, verification logs, nonce ledger) + Redis 7 (short-lived nonce/state, atomic replay protection).
- Clients: Android (
com.verifyblind.mobile) and iOS (app.verifyblind.ios) mobile apps; a browser widget for partners (verifyblind.js) and Android and iOS SDKs.
4. Threat Model
VerifyBlind rejects the assumption that "the partner is honest and the infrastructure is secure". The principal attacker profiles it defends against:
| # | Attacker | Goal | Defence |
|---|---|---|---|
| T1 | Malicious/curious partner | Learn the user's national ID number (TCKN) | Data minimisation; TCKN never reaches the partner (§2, §12) |
| T2 | Network eavesdropper / MITM | Read data in transit | TLS + end-to-end hybrid encryption (§5) |
| T3 | Attacker who seizes the relay/parent EC2 shell | Harvest plaintext identity / forge tickets | Nitro isolation + attestation; in-enclave HMAC; attestation-bound KMS trust (§6, §8, §10) |
| T4 | Fake/cloned ID card | Pass verification with an invalid identity | Passive Authentication (CSCA+CRL+DG hash) + mandatory Active Authentication (§7) |
| T5 | Presentation attack (photo/mask/screen) | Bypass the liveness check | Move sequence + biometric match on every frame + passive anti-spoof (§7) |
| T6 | Holder of a leaked/stolen ticket | Pass verification with someone else's ticket | Holder-of-key signature + live face at every verification (§8) |
| T7 | Replay attacker | Replay a valid request | Single-use nonce + atomic Redis Lua + nonce ledger (§8) |
| T8 | Fake/modified client app | A forged flow from an emulated device | Play Integrity / App Attest (§9) |
| T9 | Supply chain / malicious enclave version | Run a backdoored enclave | Developer-signed PCR0 verification (§6) |
5. Cryptographic Design
5.1 Hybrid encryption (device → enclave)
The mobile app encrypts the sensitive enrolment payload (NFC data groups, selfie, etc.) on the device:
- Content: AES-256-GCM (confidentiality + integrity/AEAD).
- Key wrapping: the one-time AES key is wrapped with the enclave's public key using RSA-OAEP-SHA256 (MGF1-SHA256).
- Decryption happens only inside the enclave; the relay cannot unwrap the wrapped key.
5.2 Signing
Signatures are produced with RSA-PSS (SHA-256, MGF1-SHA256, 32-byte salt length). This is used in the holder-of-key proof (§8) and the related signature paths.
5.3 Partner result channel (browser widget)
The browser-side verifyblind.js generates a one-time (ephemeral) RSA-OAEP key pair via the Web Crypto API. Its public key is relayed to VerifyBlind through the partner backend; the result comes back encrypted with this ephemeral public key and can be opened only with the private key held in that browser session. The partner's API key never reaches the browser (it stays at the server-side "nonce" end).
6. Hardware Root of Trust: AWS Nitro Enclave + PCR0
6.1 Isolation
The enclave runs in a hardware-isolated memory region, separate from the host machine (parent EC2). The parent machine's operating system (and any attacker who seizes it) cannot read the enclave's RAM. The enclave speaks to the outside world only over a narrow vsock channel.
6.2 Attestation and PCR0 version verification
The Nitro Security Module (NSM) produces an attestation document containing measurements of the running enclave image. This document is signed by AWS Nitro hardware, and its certificate chain roots in AWS's root authority — meaning it can be cryptographically verified that the document truly came from a Nitro enclave. VerifyBlind verifies the PCR0 (enclave image measurement) value in the document to be signed with the VerifyBlind developer key:
- The mobile app verifies the attestation document and the developer signature supplied for its PCR0 with the developer public key embedded in the app. No identity data is sent to an enclave version without such a signature (T9).
- The relay serves the signatures of the live enclave version and the previous one.
- Partners can run the same check: the attestation document and the PCR0 signature come with the verification result, and the developer public key is published at
/api/public/developer-key.
7. Identity Proof: NFC + Biometrics + Liveness
7.1 NFC Passive Authentication
The ID card's data groups (DGs) and its signed security object (SOD) are verified inside the enclave:
- CSCA + CRL verification: the country signing certificate authority chain that signed the card is verified; revocation lists (CRLs) are checked. The relevant root certificates and revocation lists are embedded in the enclave.
- DG hash binding (DG1 + DG2 + DG15): the hashes of the read data groups are verified against the signed
LDSSecurityObjectin the SOD. DG2 (the face) is mandatory and is bound slot-wise against the SOD — that is, the verified face is an inseparable part of the card's signed content.
7.2 NFC Active Authentication
The card signs a challenge with its internal private key; this proves that the chip is a genuine, un-cloned physical card. In VerifyBlind this check is mandatory: if DG15 or the active signature is missing/incorrect, verification is rejected.
7.3 Biometric match
The face embedding vector extracted from the user's live selfie (an ArcFace-based face embedding model) is compared against the DG2 face on the card via cosine similarity. This comparison runs at enrolment and again at every later verification (§8.4). The real security gate is inside the enclave (the threshold is enforced on the enclave side); the threshold on the phone side is only a pre-check for user experience and, even if bypassed by an attacker, does not break security.
7.4 Passive liveness (anti-spoof)
A passive presentation-attack (anti-spoof) detection runs on the selfie inside the enclave; if the liveness probability is below the threshold, verification is rejected. This is the security gate enforced on the enclave side against presentation attacks such as photo/screen/mask.
7.5 Active liveness (move sequence)
The user holds the phone at a single, comfortable distance and makes short moves: a blink, a smile, opening the mouth and a double blink. Which moves are asked, and in which order, is derived from the operation's nonce; the client cannot choose the sequence.
- When adding a card: five moves; the same move never comes twice in a row.
- At every verification: one move (a smile, opening the mouth or a double blink).
The phone detects the move; for each move it sends a neutral frame and a move frame to the enclave. The enclave checks that the proof matches the requested sequence and that the face in every frame belongs to the card holder. A photo cannot make the moves, and making them with someone else's face fails the identity check.
8. Session / Ticket Security
After a successful enrolment, the enclave issues a signed Ticket stored on the user's device. Subsequent verifications (login) rely on this ticket. Four mechanisms stop the ticket from being a "bearer token":
8.1 Ticket-MAC (in-enclave HMAC) — against ticket forgery
The ticket's integrity is verified with a symmetric HMAC key held only inside the enclave. This key is encrypted with a KMS wrapping key (CMK) and stored in wrapped form; at every enclave startup it is unwrapped via an attestation-bound KMS decrypt operation — meaning only an enclave with the correct PCR0 measurement can decrypt the key. Thus the secret that verifies ticket integrity never leaves the enclave.
8.2 Holder-of-key — against stolen tickets
The ticket owner's key pair is generated on the device during enrolment. On Android the private key is held in Android Keystore (in the hardware-protected area, TEE, on devices that support it); on iOS it is held in the Keychain, bound to that device only. It is not included in backups or moved to other devices, and it can be used only after the user approves with biometrics or the screen lock.
The owner's public key is carried inside the ticket, within the MAC scope. At login the device signs a login-specific canonical message (nonce + key fingerprint + timestamp) with that private key, using RSA-PSS/SHA-256; the enclave verifies this signature against the MAC-verified public key, within a narrow time window. Result: a leaked ticket cannot be used by anyone who does not also hold its corresponding private key (T6).
8.3 Nonce / replay protection
Every flow is bound to a single-use nonce. Spending the nonce is done as an atomic operation, so two requests racing for the same nonce (TOCTOU) are prevented. A separate nonce ledger is kept for durable auditing (T7).
8.4 Live face at every verification
At enrolment, the face photo extracted from the SOD-verified DG2 is sealed into the ticket within the MAC scope. At every verification the enclave compares the live frame from the phone with this reference, runs the passive anti-spoof check and verifies the one-move sequence (§7.5). If any check fails, the verification is rejected. The goal is to stop someone who can unlock the phone from verifying on the card holder's behalf.
9. Client Integrity: Play Integrity / App Attest
The enrolment and verification flows carry a platform attestation that the request comes from the genuine, untampered VerifyBlind mobile app:
- Android: app and device integrity, and the app's signing certificate, are verified via the Google Play Integrity API.
- iOS: Apple App Attest (challenge → enroll → assertion; the CBOR + X.509 chain is verified to the Apple Root CA, with an ECDSA assertion and a strictly increasing counter for replay resistance).
- Routing is done per platform (iOS → Apple App Attest, other → Play Integrity).
10. Parent Isolation and KMS Key Trust
Nitro isolates the enclave's RAM from the parent; however, the enclave obtains its AWS credentials from the parent's IMDS vsock-proxy → at the IAM level, the enclave and the parent are indistinguishable. The only distinguishing factor is attestation. KMS usage is designed to preserve security even if the parent is compromised:
- KMS connection: on the enclave↔KMS connection, the TLS certificate chain and hostname match are strictly verified. Even if the parent is seized and network traffic is redirected to the attacker, a rogue endpoint cannot present a valid KMS certificate, so the sensitive data being processed cannot be exfiltrated.
- Ticket integrity: the ticket is signed and verified with an in-enclave symmetric HMAC (signer = verifier = enclave). The wrapping key is unwrapped only via an attestation-bound decrypt operation; the production role holds only minimal, key-scoped permissions — it has no authority to sign tickets, create keys or change key policy. The HMAC secret that produces the pseudonymous codes (
user_id,nsbd_id,doc_id) is likewise stored wrapped by KMS, unwrapped only inside the enclave via an attestation-bound decrypt, and the codes are computed inside the enclave → even an attacker who seizes the partner database cannot reverseuser_idback to a TCKN.
11. Operational Security
- Audit logging: every verification operation is written to the audit log and event stream without revealing identity.
- Monitoring and incident response: infrastructure and administrative events are logged centrally and raise alerts on anomalies. A ready incident process exists to cut off access, rotate secrets and collect immutable evidence during a security event.
- Backup / recovery: database backups are stored encrypted and version-protected; restore is drilled regularly.
12. KVKK / Data Minimisation
- No identity data reaches the partner and none is stored by VerifyBlind.
- The partner receives only the result of the proposition it asked and, if requested, pseudonymous codes:
user_id— a TCKN-derived pseudonymous code (to recognise a returning person without storing them);nsbd_id— a biographic person bucket (stable even across card renewal);doc_id— a document-based fixed code.
- KVKK infrastructure: explicit consent records, data-subject requests (access/erasure/portability/objection/rectification), breach log, table-based retention rules.
- Outcome: because the most sensitive personal data (TCKN, ID photo) is never transmitted to the partner and never persistently stored anywhere, the partner's data-processing burden and risk surface under KVKK are markedly reduced. (Final compliance depends on the partner's processing purpose; this document is not legal advice.)
13. Verifiability
VerifyBlind's core stance is not "trust us" but "verify the math": which code the system runs can be independently tested.
- Enclave — reproducible build + attestation (the core guarantee): the enclave source code is open (
github.com/VerifyBlind/VerifyBlind-Enclave). The enclave image compiled from this source produces a deterministicPCR0fingerprint — if even a single character in the code changes, PCR0 changes entirely. An independent auditor can: (1) clone the source, compile it and note the resulting PCR0; (2) obtain the live attestation document, signed by AWS Nitro hardware, from the running system (§6); (3) compare the two PCR0s. If they match, the enclave running in production is exactly that published code. Thus the claim "VerifyBlind cannot see/store raw data" rests not on a promise but on a measurement anyone can reproduce. In addition, the enclave "seals" the key it generates at each startup into the attestation document → a document belonging to an old/other enclave cannot be replayed. - Android — reproducible build: every release is built on GitHub Actions from the public source commit (
github.com/VerifyBlind/VerifyBlind-Android), and the same commit is built a second time to confirm that the code (DEX) files come out identical. Public verification scripts compare the DEX files of the app on the phone with the hash list published for that release. It can also be tested via Play "code transparency" (bundletool check-transparency). - iOS — build provenance: the FairPlay encryption in App Store distribution prevents the binary from being verified by bit-for-bit reproduction from the device. Instead, every release is built on GitHub Actions from the public source commit (
github.com/VerifyBlind/VerifyBlind-iOS) and published with Sigstore-signed build provenance. Public verification scripts for Windows, macOS and Linux compare the published IPA's hash with the release manifest and verify the provenance record with cosign. That the app on the device is the genuine App Store build is checked by the server with App Attest on every protected request (§9).
These mechanisms are explained step by step, in plain language for end users, on the transparency and zero-knowledge pages.
14. Glossary
- TCKN
- Turkish national identity number.
- Enclave / Nitro Enclave
- A verifiable compute environment, hardware-isolated from the host machine.
- Attestation
- A hardware-signed document containing measurements of the running code.
- PCR0
- The enclave image measurement; the cryptographic answer to "which code is running".
- NFC Passive Authentication
- Verifying the card's data against the country CA's signature.
- NFC Active Authentication
- The chip proving it is not a clone by signing a challenge with its private key.
- Holder-of-key
- The presenter of a ticket proving, by signature, that they also hold the private key bound to the ticket.
- Ticket-MAC
- Verifying ticket integrity with a symmetric HMAC known only to the enclave.
- Nonce
- A single-use random value that prevents replay.
This document is a draft and will be updated as the product evolves.