MRZ OACI 9303 : comment valider cartes d'identité et passeports
Formats TD1, TD2 et TD3, chiffres de contrôle pondérés 7-3-1 modulo 10, exemple avec le spécimen officiel de l'OACI et code JavaScript pour valider une MRZ.
Par L'équipe Constaia8 min de lecture
La bande de lecture automatique (MRZ, pour machine readable zone) désigne les deux ou trois lignes remplies de < en bas de la page d'un passeport ou au verso d'une carte d'identité. Elle est conçue pour être lue par une machine sans erreur, d'où ses chiffres de contrôle : si l'OCR se trompe sur un caractère, le calcul ne tombe plus juste.
Cet article présente les formats, l'algorithme des chiffres de contrôle avec un exemple réel tiré du document de l'OACI, du code pour valider une MRZ et, surtout, ce qu'une MRZ valide ne prouve pas.
La norme : le Doc 9303 de l'OACI
La MRZ est définie par le Doc 9303 de l'OACI (Organisation de l'aviation civile internationale, 8e édition). La partie 3 fixe ce qui est commun à tous les documents ; les parties 4 à 7 détaillent chaque format. L'essentiel :
- Seuls A–Z, 0–9 et le caractère de remplissage
<sont utilisés, imprimés dans la police OCR-B. - Les dates s'écrivent AAMMJJ : le 12 juillet 1942 devient
420712. - Les noms sont translittérés sans signes diacritiques. Par exemple, la table de l'OACI permet d'écrire le Ñ espagnol
NouNXX.
Trois formats
| Format | Lignes × caractères | Usage type | Partie du Doc 9303 |
|---|---|---|---|
| TD1 | 3 × 30 | Cartes d'identité (format carte bancaire), comme le DNI espagnol | Partie 5 |
| TD2 | 2 × 36 | Certains documents officiels de format intermédiaire | Partie 6 |
| TD3 | 2 × 44 | Passeports | Partie 4 |
Le TD2 ressemble au TD3 avec moins de place : un champ nom plus court et un chiffre de contrôle en moins. En pratique, dans les formulaires européens, vous rencontrerez surtout du TD1 (cartes d'identité) et du TD3 (passeports).
Le chiffre de contrôle : pondération 7-3-1, modulo 10
La partie 3 (section 4.9) décrit le calcul :
- Chaque caractère a une valeur : les chiffres gardent leur valeur,
<vaut 0 et les lettres vont de A = 10 à Z = 35. - On multiplie chaque valeur par un poids qui se répète : 7, 3, 1, 7, 3, 1…
- On additionne les produits.
- Le chiffre de contrôle est le reste de la division par 10.
| A | B | C | D | E | F | G | H | I | J | K | L | M |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
L'exemple de l'appendice A de la partie 3 : la date du 27 juillet 1952 (520727).
| Caractère | 5 | 2 | 0 | 7 | 2 | 7 |
|---|---|---|---|---|---|---|
| Poids | 7 | 3 | 1 | 7 | 3 | 1 |
| Produit | 35 | 6 | 0 | 49 | 6 | 7 |
Somme : 103. Reste de la division par 10 : 3. Dans la MRZ, on écrit 5207273.
Exemple détaillé : le passeport spécimen de l'OACI (TD3)
L'appendice A de la partie 4 présente un passeport fictif de l'État d'« Utopie » (code UTO) au nom d'Anna Maria Eriksson :
PPUTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<
L898902C36UTO7408122F3404159ZE184226B<<<<<16La première ligne contient le type de document (PP), l'État émetteur (UTO) et le nom : nom de famille, <<, puis prénoms séparés par <. La seconde ligne porte les données et leurs chiffres de contrôle :
| Positions | Champ | Valeur | Contrôle |
|---|---|---|---|
| 1–9 | Numéro du document | L898902C3 | 10 : 6 |
| 11–13 | Nationalité | UTO | — |
| 14–19 | Date de naissance | 740812 | 20 : 2 |
| 21 | Sexe | F | — |
| 22–27 | Date d'expiration | 340415 | 28 : 9 |
| 29–42 | Numéro personnel / données facultatives | ZE184226B<<<<< | 43 : 1 |
| 44 | Contrôle composite | — | 6 |
Vérifions le numéro du document, L898902C3 :
| Caractère | L | 8 | 9 | 8 | 9 | 0 | 2 | C | 3 |
|---|---|---|---|---|---|---|---|---|---|
| Valeur | 21 | 8 | 9 | 8 | 9 | 0 | 2 | 12 | 3 |
| Poids | 7 | 3 | 1 | 7 | 3 | 1 | 7 | 3 | 1 |
| Produit | 147 | 24 | 9 | 56 | 27 | 0 | 14 | 36 | 3 |
Somme : 316 → reste 6. Cela correspond à la position 10.
De même, la date de naissance 740812 donne 122 (contrôle 2), l'expiration 340415 donne 69 (contrôle 9) et le numéro personnel donne 401 (contrôle 1).
Le chiffre composite
Le dernier caractère protège toute la ligne. Il se calcule sur les positions 1–10, 14–20 et 22–43 de la seconde ligne, c'est-à-dire les champs dotés de leur propre chiffre de contrôle, en sautant la nationalité et le sexe :
L898902C36 + 7408122 + 3404159ZE184226B<<<<<1La somme pondérée vaut 896 → contrôle composite 6, dernier caractère de la ligne.
Si le champ du numéro personnel est vide (uniquement des <), son chiffre de contrôle peut être < ou 0. Prévoyez les deux cas.
TD1 : le format des cartes d'identité
Le spécimen de la partie 5 est une carte de la même titulaire :
I<UTOD231458907<<<<<<<<<<<<<<<
7408122F1204159UTO<<<<<<<<<<<6
ERIKSSON<<ANNA<MARIA<<<<<<<<<<- Ligne 1 : type (
I<), État émetteur (UTO), numéro du document en positions 6–14 (D23145890) et son contrôle en 15 (7) ; de 16 à 30, données facultatives. - Ligne 2 : naissance (1–6) et contrôle (7), sexe (8), expiration (9–14) et contrôle (15), nationalité (16–18), données facultatives (19–29) et contrôle composite (30).
- Ligne 3 : le nom.
Le composite du TD1 se calcule sur les positions 6–30 de la ligne 1 et 1–7, 9–15 et 19–29 de la ligne 2. Dans le spécimen, il vaut 6.
Un détail : si le numéro du document dépasse 9 caractères, la position 15 contient un < et le numéro se poursuit dans la zone des données facultatives, avec son chiffre de contrôle à la fin.
Code JavaScript
const WEIGHTS = [7, 3, 1];
function charValue(c: string): number {
if (c === "<") return 0;
if (c >= "0" && c <= "9") return c.charCodeAt(0) - 48; // 0–9
if (c >= "A" && c <= "Z") return c.charCodeAt(0) - 55; // A=10 … Z=35
throw new Error(`Caractère invalide dans la MRZ : ${c}`);
}
export function checkDigit(data: string): string {
let sum = 0;
for (let i = 0; i < data.length; i++) sum += charValue(data[i]) * WEIGHTS[i % 3];
return String(sum % 10);
}
export function validateTd3(line1: string, line2: string) {
if (line1.length !== 44 || line2.length !== 44) throw new Error("TD3 : deux lignes de 44");
const fields: Record<string, [string, string]> = {
document_number: [line2.slice(0, 9), line2[9]],
birth_date: [line2.slice(13, 19), line2[19]],
expiry_date: [line2.slice(21, 27), line2[27]],
personal_number: [line2.slice(28, 42), line2[42]],
composite: [line2.slice(0, 10) + line2.slice(13, 20) + line2.slice(21, 43), line2[43]],
};
return Object.fromEntries(
Object.entries(fields).map(([name, [data, digit]]) => [name, checkDigit(data) === digit]),
);
}
validateTd3(
"PPUTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<",
"L898902C36UTO7408122F3404159ZE184226B<<<<<16",
);
// { document_number: true, birth_date: true, expiry_date: true,
// personal_number: true, composite: true }Si vous remplacez l'expiration par 340416, le contrôle de l'expiration et le composite échouent. C'est ainsi qu'on repère une erreur de lecture (ou une retouche maladroite).
Ce que ce code ne couvre pas
- Les erreurs d'OCR :
O/0,I/1,B/8etS/5sont souvent confondus. Dans les champs forcément numériques (dates, chiffres de contrôle), vous pouvez corriger sans risque ; dans le numéro du document, alphanumérique, vous pouvez essayer des substitutions et garder celle qui tombe juste. - Le siècle :
74peut signifier 1974 ou 2074. Pour la naissance, retenez l'année qui n'est pas dans le futur ; pour l'expiration, celle qui est cohérente avec la durée de validité du document. - Les lignes coupées : si une photo coupe la fin d'une ligne, complétez avec des
<uniquement si vous savez qu'il manquait des caractères de remplissage, et signalez le résultat comme douteux.
Cohérence entre la MRZ et la zone visuelle
Une MRZ cohérente ne suffit pas : elle doit correspondre à ce qui est imprimé dans la zone d'inspection visuelle (VIZ). Comparez au minimum :
- le numéro du document,
- les dates de naissance et d'expiration,
- le sexe et la nationalité,
- le nom et les prénoms, en tenant compte de la translittération (sans accents,
<pour l'espace, Ñ enNouNXX) et de la troncature possible des noms longs.
Une MRZ correcte dont les données diffèrent de celles imprimées est un signal clair pour contrôler le document à la main.
Avertissement : un checksum ne prouve pas l'authenticité
L'algorithme est public et simple. Il existe des générateurs publics qui produisent des MRZ dont tous les chiffres de contrôle sont corrects. Par conséquent :
- Une MRZ valide montre seulement que les caractères sont cohérents entre eux.
- Elle ne montre pas que le document existe, qu'il n'a pas été annulé ni qu'il appartient à la personne qui le présente.
- L'authenticité se vérifie par d'autres moyens (par exemple les éléments de sécurité physiques ou la puce du document), qui dépassent ce que montre une photo.
Comment fait Constaia
Quand vous analysez un DNI, un NIE ou un passeport, Constaia lit la MRZ par OCR, corrige les confusions typiques et renvoie deux contrôles déterministes : mrz_checksums (tous les chiffres de contrôle) et mrz_matches_visual (la MRZ correspond aux données imprimées). Si quelque chose ne colle pas, le verdict passe à invalid ou review, avec le motif.
curl https://api.constaia.com/v1/analyze \
-H "Authorization: Bearer ck_test_..." \
-F file=@passport.jpg \
-F 'options={"expect":"passport","checks":{"not_expired":true},"storage":"none"}'Avec une clé ck_test_… et le fichier passport.jpg, vous obtenez une réponse simulée sans consommer de crédits. Le validateur MRZ fait partie du paquet open source @constaia/validators. Et nous le répétons : Constaia n'est pas un KYC biométrique, et un checksum correct ne prouve pas qu'un document est authentique. Plus d'informations dans Checks.
En résumé
- TD1 = 3 × 30 (cartes d'identité), TD2 = 2 × 36, TD3 = 2 × 44 (passeports).
- Chiffre de contrôle : valeurs (A = 10 … Z = 35,
<= 0), poids 7-3-1, somme, modulo 10. - Validez chaque champ et le composite, puis comparez la MRZ à la zone visuelle.
- Un checksum correct ne prouve pas l'authenticité.
Envie de tester une MRZ précise ? Utilisez le lecteur de MRZ gratuit. Pour l'intégrer à votre produit, créez un compte gratuit avec 250 crédits par mois.
Sources
- 01OACI Doc 9303, 8e éd., partie 3 : spécifications communes à tous les DVLM (anglais)
- 02OACI Doc 9303, 8e éd., partie 4 : passeports (TD3) (anglais)
- 03OACI Doc 9303, 8e éd., partie 5 : documents de format TD1 (anglais)
- 04Wikipedia — Machine-readable passport
- 05Exemple public de générateur de MRZ (GitHub)