Docs
Verification

NFC Identity Reading

Read and verify identity documents via NFC chip (passports, national ID cards)

NFC identity reading extracts identity data directly from the chip embedded in passports and national ID cards. The chip stores the same data printed on the document, plus a high-resolution portrait photo, all signed by the issuing country's certificate authority.

Supported documents

DocumentProtocolNotes
Passports (ICAO 9303)BAC (Basic Access Control)Document number, date of birth, and expiry required for access
Spanish DNIeISO 14443Standard contactless interface
Other EU national ID cardsISO 14443 / PACESupport varies by issuing country

How it works

1. Optical data capture

Before reading the NFC chip, the app captures the Machine Readable Zone (MRZ) from the document. The MRZ provides the access keys:

MRZ fieldUsed for
Document numberBAC key derivation
Date of birthBAC key derivation
Date of expiryBAC key derivation

For passports, the document number must be padded to 9 characters with < for correct BAC key computation.

2. NFC chip access

The access protocol depends on the document type:

BAC (Basic Access Control): Used by passports. The reader derives session keys from MRZ data to authenticate and establish an encrypted channel.

PACE (Password Authenticated Connection Establishment): Used by newer EU ID cards. Provides stronger key agreement through Diffie-Hellman.

3. Data extraction

Once the secure channel is established, the reader extracts data groups from the chip:

Data GroupContents
DG1MRZ data (name, nationality, dates)
DG2Biometric portrait photo (JPEG/JPEG2000)
DG7Signature image (if present)
DG11Additional personal details (address, phone)
DG14Security infos for Chip Authentication
SODDocument Security Object (signed hashes of all DGs)

4. Passive authentication

Passive authentication verifies that the chip data has not been tampered with:

  1. Extract the Document Security Object (SOD) from the chip.
  2. Verify the SOD signature against the Document Signer Certificate embedded in it.
  3. Verify the Document Signer Certificate against the Country Signing Certificate Authority (CSCA) master list.
  4. Compare the hash of each data group against the hash stored in the SOD.

If all checks pass, you can be certain the data was written by the issuing authority and has not been modified.


CSCA master list

Verifiables serves the CSCA master list used for passive authentication:

GET /nfc-identity/csca

Returns the consolidated CSCA master list (the union of several national master lists) as PEM-encoded certificates (Content-Type: application/x-pem-file). The list contains 580+ certificates from countries worldwide. Responses are cacheable for 1 day (Cache-Control: public, max-age=86400).

Authentication: None (public endpoint)

curl https://api.verifiables.com/nfc-identity/csca \
  -o csca_master_list.pem

Returns 503 with { "error": "CSCA master list unavailable" } if the list cannot be served.


Client-side read result

The NFC chip is read and verified on the device by the mobile SDK; the reading itself does not go through the API. After a successful read, the SDK produces a result like:

{
  "status": "valid",
  "document": {
    "type": "passport",
    "issuingCountry": "FRA",
    "documentNumber": "12AB34567",
    "expiryDate": "2030-05-15"
  },
  "identity": {
    "givenNames": "Marie Claire",
    "familyName": "Dupont",
    "dateOfBirth": "1990-01-15",
    "nationality": "FRA",
    "portrait": "data:image/jpeg;base64,/9j/4AAQ..."
  },
  "verification": [
    { "status": "OK", "check": "SOD signature valid" },
    { "status": "OK", "check": "Document Signer certificate valid" },
    { "status": "OK", "check": "CSCA chain valid" },
    { "status": "OK", "check": "Data group hashes match" }
  ]
}

This object is produced by the mobile SDK on the device, not returned by any API endpoint.

FieldDescription
documentDocument metadata (type, issuing country, number, expiry)
identityExtracted personal data and portrait
verificationArray of passive authentication checks

Verifying a read with the API

POST /nfc-identity/verify

Passive authentication of the files your app read from the chip of a passport or ID card, on our servers. Send the raw files as read, base64 encoded; the holder's data is read from the signed data groups and nothing else in the request is trusted. The API checks that every data group matches its hash in the SOD, that the SOD's signed attributes digest its content, that the document signer's signature holds, and that a CSCA of the master list issued the document signer.

Authentication: API key (publishable or secret), or from a mobile app an SDK token minted by your backend (see Native SDK). Under an SDK token minted with requirePresence (the default), a read whose chip presence was not proven answers invalid, with the chip.presence check failed for presence_required.

{
  "sessionId": "chip-session-42",
  "sod": "dyCCB...",
  "dg1": "YVtfH1hQ...",
  "dg11": "a0Jc...",
  "attempt": { "number": 1 }
}
FieldTypeRequiredDescription
sodbase64YesEF.SOD as read (with or without its 0x77 tag), 32 kB max
dg1base64YesEF.DG1 (the MRZ), 1 kB max
dg2base64NoEF.DG2 (face image), checked against its hash, 256 kB max
dg11base64NoEF.DG11 (full name, place of birth), checked against its hash, 8 kB max
dg14base64NoEF.DG14, checked against its hash, 8 kB max
sessionIdstringNoYour id for the reading session; the attempts of one session are counted as one
attemptobjectNo{ number, reason? }, for your own tracing

Response 200

{
  "sessionId": "chip-session-42",
  "verdict": "valid",
  "checks": [
    { "id": "sod.readable", "status": "pass" },
    { "id": "dg.hashes", "status": "pass", "detail": "DG1, DG11" },
    { "id": "sod.signature", "status": "pass", "detail": "Document Signer 7" },
    { "id": "dsc.issuer", "status": "pass", "detail": "CSCA Utopia" },
    { "id": "dg1.mrz", "status": "pass", "detail": "TD3" },
    { "id": "dg11.read", "status": "pass" }
  ],
  "claims": {
    "documentCode": "P",
    "documentType": "passport",
    "documentNumber": "L898902C3",
    "issuingState": "UTO",
    "nationality": "UTO",
    "familyName": "ERIKSSON",
    "givenNames": "ANNA MARIA",
    "birthDate": "1974-08-12",
    "expiryDate": "2012-04-15",
    "sex": "F",
    "placeOfBirth": "ZENITH",
    "personalNumber": null,
    "mrz": ["P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<", "L898902C36UTO7408122F1204159ZE184226B<<<<<10"],
    "nameSource": "dg11"
  },
  "documentType": "passport",
  "issuingState": "UTO",
  "expiry": "2012-04-15",
  "authorityCountry": "UT"
}

verdict is valid only when every check passes and the document signer chains to a CSCA of the master list; claims is null otherwise. A verdict of invalid is still a verdict (the data was checked). Names and the birth date come from DG11 when you send it (full names, four-digit year), from the MRZ otherwise. The portrait is not returned.

Returns 422 with { "error": "sod_unreadable", ... } when the SOD is not an EF.SOD, 503 with { "error": "trust_store_unavailable", ... } when the CSCA master list cannot be loaded (no verdict, retry later), and 422 with { "error": "Validation failed", "issues": [...] } on a malformed body.

Passive authentication proves the data was signed by the issuing State. It does not prove the chip was present when the files were sent.


Submitting a read to the API (legacy)

POST /nfc-identity/read

Legacy. Stores fields your app sends, without any verification; use POST /nfc-identity/verify instead. It does not read the chip and does not return identity data.

Authentication: API key (publishable or secret)

Request body

{
  "documentType": "passport",
  "firstName": "Marie Claire",
  "lastName": "Dupont",
  "birthDate": "1990-01-15",
  "documentNumber": "12AB34567",
  "expiryDate": "2030-05-15",
  "nationality": "FRA"
}
FieldTypeRequiredDescription
documentType"passport" | "cni"YesDocument type
firstNamestringYesGiven names from the chip
lastNamestringYesFamily name from the chip
birthDatestringYesYYYY-MM-DD
documentNumberstringYesDocument number
expiryDatestringYesYYYY-MM-DD
genderstringNoGender
nationalitystringNoNationality code
mrzstringNoRaw MRZ string
identityNostringNoNational identity number
placeOfBirthstringNoPlace of birth

Response 201

{
  "sessionId": "550e8400-e29b-41d4-a716-446655440000",
  "expiresIn": 900
}

The stored read expires after 900 seconds (15 minutes). Returns 422 with { "error": "Validation failed", "issues": [...] } if the body does not match the schema.


Security considerations

  • Chip cloning protection: Active Authentication (AA) or Chip Authentication (CA) can detect cloned chips, but not all documents support it. Passive authentication alone proves data integrity, not chip authenticity.
  • MRZ as access key: The MRZ data acts as a shared secret. Physical possession of the document is required to read the chip.
  • Portrait matching: The chip portrait is higher quality than the printed photo and can be used for biometric face matching.
  • CSCA trust: The CSCA master list is the root of trust. Verifiables builds it from several national master lists and refreshes it daily.