Gerege eID — Identifiers (ETSI ID and registration number)¶
The system has two main numbers for identifying a person/organization: the ETSI identifier (the standard certificate key) and the registration number (РД) (the Mongolian citizen number). This document explains their structure and meaning.
1. ETSI identifier — etsi_identifier¶
ETSI = European Telecommunications Standards Institute. Our etsi_identifier is its
ETSI EN 319 412-1 standard "semantics identifier" — in other words, the
international standard format for writing a national number into a certificate. It is eIDAS-compliant
and wire-compatible with Estonia's Smart-ID semanticsIdentifier (we adapted it).
Format¶
PNOMN-95103015
└┬┘└┬┘ └───┬──┘
│ │ └── actual number (for a natural person, civil_id — digits only, not the РД)
│ └── country — ISO 3166-1 alpha-2 (MN = Mongolia)
└── subject type (3 characters)
| Prefix | Meaning | Owner | Example |
|---|---|---|---|
| PNO | Person Number (national personal number) | natural person (Natural Person) | PNOMN-95103015 |
| NTR | National Trade Register | organization (Legal Person) | NTRMN-1234567 |
Why¶
- Stable key: every citizen's devices / certificates / sessions are linked by this ID
(
users.etsi_identifieris the primary key). - Standards compliance: the procedure for writing a national ID into an eIDAS qualified cert; European RPs understand it.
- Type embedded in the prefix:
PNO↔NTRdistinguishes natural person/organization → for automatically selecting the issuing CA (domain.SubjectTypeForEtsi, Personal/Organization CA). - Inner value =
civil_id(digits only), not the РД: the cert'sSERIALNUMBERis an X.509 PrintableString and therefore cannot contain a Cyrillic РД. Hence a natural person getsPNOMN-<civil_id>(etsi = "PNOMN-" + civilID); the РД is a separate attribute inusers, NOT in the cert.
In code:
persistence.UserAccount.EtsiIdentifier,domain.SubjectTypeForEtsi, ETSI construction incrypto_wired.go("PNOMN-" + civilID).
2. Registration number (РД) — registration_number¶
The Mongolian citizen's registration number: 2 Cyrillic letters + 8 digits (e.g., УБ95103015).
Structure (nominal)¶
У Б 9 5 1 0 3 0 1 5
└┬┘ └──────┬──────┘
│ └── 8 digits (nominally YYMMDD + sequence)
└── first letters of the father's/given name (2)
Date of birth/gender — source and deriving from the РД¶
date_of_birth and gender come first and foremost directly from the KYC verifier (DAN/eMongolia)
(service.KycResult.DateOfBirth, .Gender; from the DAN callback via NormalizeGender).
Some sources (G-Sign) do not return these — the cert subject provides only identity. In that
case the system derives them from the РД (util.BirthAndGenderFromRegNo, gsign/kycverifier.go):
- Date: first 6 digits = YYMMDD. Month 21..32 → after 2000 (year 2000+YY, month MM-20);
month 01..12 → before 2000 (year 1900+YY). Invalid calendar date → nil.
- Gender: the 2nd digit from the end odd → "M", even → "F" (independent of the date).
If digits are missing/malformed, each is left empty (nil/"") — empty is better than a wrong guess.
3. Other related numbers¶
| Field | Description | Source |
|---|---|---|
civil_id |
Citizen ID card number (need not match the РД) | KYC (KycResult.CivilID) |
| Organization registration number | Legal entity's state registration → etsi NTRMN-<...>, organizationIdentifier (OID 2.5.4.97) |
KYC (KycResult.OrgRegister) |
document_number |
Each device's unique UUID (contains no РД — privacy). Smart-ID documentNumber | server-generated |
certificates.serial_number |
X.509 serial number (decimal); hex in OCSP/CRL | CA |
Principle: citizen/organization identity attributes (civil_id, org register, name) all come from the KYC verifier (DAN/eMongolia) — not derived from or fabricated out of numbers. If the source provides none, it is left empty. (DOB/gender usually come from KYC, but when the source does not return them they are derived from the РД — see §2.)
Summary¶
- ETSI identifier = "the standard format for writing a national number into a certificate" (
PNOMN-…natural person,NTRMN-…organization). The name is odd but the meaning is simple. - РД = the "2 letters + 8 digits" citizen number. Date of birth/gender usually come directly from the
KYC source; otherwise they are derived from the РД (
util.BirthAndGenderFromRegNo).
§Case — the identity-text upper/lowercase RULE¶
Rule: the DB canonical form of identity text is LOWERCASE; all searching/filtering/comparing is done lowercase; display is in standard form. The user may enter their registration number in any case.
Covered fields (human-entered / KYC-sourced identity text):
users.registration_number (РД), civil_id, given_name, surname, full_name.
| Action | Rule | Implementation |
|---|---|---|
| Store | trim + lower (LOWERCASE canonical) | util.NormalizeIdentity; recordUserAndCert; KYC normalizeRegNo=lower |
| Search / lookup / compare | normalize the input to lowercase, then compare (case-insensitive) | pg FindByIdentifier/Search reg_no → lower; inmem already lower; DAN callback matching lower-lower |
| Display | РД → UPPERCASE (util.DisplayRegNo); Cyrillic name → Title Case (util.TitleCaseName) |
PersonBlock, PersonSummary, admin userView, KYC preview |
NOT covered (has its own wire/PKI canonical form — do not touch):
- etsi_identifier — ETSI EN 319 412-1 canonical (PNOMN-…/NTRMN-…); lookup/compare UPPERCASE.
- Cert SERIALNUMBER / subject DN, given_name_latin/surname_latin — ICAO/eIDAS UPPERCASE.
- document_number (UUID), API key hash, org org_register (digits), org name (proper noun).
Why: each KYC source returns a different case (DAN "ЭРДЭНЭБАТ", G-Sign "баярсайхан"), and the user writes their РД as "УБ..." or "уб...". With a single canonical form (lowercase), lookup/dedup are consistent; for display it is converted to standard form. Latin/etsi/cert have their own canonical form, so they are not subject to this rule.