Skip to content

eID 2-cert — production-environment validation (2026-06-18)

All core flows of the Smart-ID / eIDAS model have been verified running end-to-end on a real iPhone + staging backend (rp-api.eidmongolia.mn).

Validated flows

Flow Key / PIN Cert Evidence
Enrollment (DAN KYC → liveness → distributed keygen → 2 cert) sign + auth SIGN + AUTH Real phone: users (civil_id), 2 ACTIVE cert, device (has_sign+has_auth)
Login (web login, QR) PIN1 · authentication clientAuth Web "Successfully logged in" + identity
Contract signing (web sign, QR) PIN2 · signing · non-repudiation QUALIFIED Web "Signed the contract", COMPLETE/OK

Key architectural decisions

  • Dual-key 2-cert. Every citizen has TWO independent threshold ECDSA keys: Authentication (PIN1, clientAuth, login) and Signing (PIN2, contentCommitment non-repudiation, legally binding signature). One phone, one documentNumber, two Keychain slots. Enroll: 2 keygen (fast via prewarm), 2 cert; the signing/auth flow loads its respective key (credentialFor(flow) backend, approve(authentication:) iOS).
  • Identity = civil_id. Cert/etsi is PNOMN-<civil_id> (digits only). reg_no (РД, Cyrillic "МА...") does NOT go into the cert — it is a separate attribute. The DAN callback sends civil_id + reg_no + image + identity. DOB/gender go into the SDA extension (RFC 3739).
  • DAN secure binding. The dan.gerege.mn callback does not send state (only reg_no), so the state is embedded in the callback_url and the callback is reliably tied to the specific session (reg_no is only a cross-check) — preventing the identity-takeover race.
  • Liveness. The DAN reference photo vs. selfie is compared via the InsightFace ArcFace (buffalo_l) service (/v1/compare, cosine ≥ 0.6). iOS uses the full camera (Vision, with DAN framing).

Components

  • Backend (Go): service/dan (KYC + liveness + photo), crypto/ca (Profile + SDA), service/memory/crypto_wired (dual-key enroll, FK-correct ordering), domain (User/KYC FSM).
  • iOS (Swift SDK + SwiftUI): reference DAN screens (phone→DAN→liveness→PIN×2→keygen), dual-key enroll, FaceVerify (ArcFace, %), RoundTripOverlay, Main Dashboard.
  • Web (Next.js RP demo): login (auth) + sign (PIN2) flow.
  • Staging deploy + operational facts: deploy/, memory/staging-ops-gotchas.

Remaining for production (TODO)

  • HSM-backed CA (currently staging ephemeral self-signed) — docs/PKI_CA_ONBOARDING.md.
  • Org / legal-person (NTRMN-) enrollment.
  • Postgres: dan_replay_cache, PII purge sweep (currently in-memory session).
  • Enrich the dashboard (pending-sign inbox, settings).