Skip to content

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_identifier is 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: PNONTR distinguishes natural person/organization → for automatically selecting the issuing CA (domain.SubjectTypeForEtsi, Personal/Organization CA).
  • Inner value = civil_id (digits only), not the РД: the cert's SERIALNUMBER is an X.509 PrintableString and therefore cannot contain a Cyrillic РД. Hence a natural person gets PNOMN-<civil_id> (etsi = "PNOMN-" + civilID); the РД is a separate attribute in users, NOT in the cert.

In code: persistence.UserAccount.EtsiIdentifier, domain.SubjectTypeForEtsi, ETSI construction in crypto_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.


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.