Au mois de mars 2025, le réseau de transports Tisséo a introduit un nouveau support de titre de transport remplaçant l'ancien support magnétique. Les nouveaux tickets, similaires à vue d'œil, offrent deux avantages : ils ne se démagnétisent pas et permettent le rechargement plusieurs fois. Utilisatrice occasionnelle du réseau, j'en ai vite profité pour faire remplacer mon ancien carnet magnétique de 10 trajets qui avait arrêté de fonctionner au bout de 5 validations par un ticket rechargeable.
Contrairement à la RATP/IDFM qui a vraisemblablement réutilisé la technologie Calypso des pass Navigo pour les supports Navigo Easy le réseau Tisséo comme d'autres villes en France a choisi une solution moins chère avec ces tickets en carton : ils sont vendus au prix de 20 centimes en plus du prix des titres. Les cartes Navigo Easy sont elles vendues 2€ à Paris. L'inconvénient du ticket en carton, en plus de sa relative fragilité matérielle, est le nombre limité de recharges : Tisséo indique plus de 30 fois !
La (relative) simplicité technique de ces nouveaux tickets en fait un excellent cas d'exemple pédagodique du fonctionnement du NFC ! Cette page va surtout se concentrer sur la manière dont sont codées les données et comment leur intégrité en est assurée.
Les données d'un tag ST25TB
Chaque titre en carton contient un tag NFC ST25TB de STMicroelectronics d'une capacité de 512 bits. La mémoire est divisée en 16 blocs de 32 bits sur lesquels l'écriture est libre à une subtilité près.
Trois blocs supplémentaires sont présents :
- deux blocs UID0 et UID1 en lecture seule sur lesquels sont inscrits un identifiant unique de 64 bits, le numéro est imprimé en décimal à l'arrière du ticket en carton et semble être séquentiel dans l'ordre de fabrication ;
- un bloc OTP (one time programmable) dont les bits 16 à 31 (en comptant à partir de zéro) définissent l'état de lecture seule des blocs 0 à 15 respectivement. Par défaut à 1, le passage de ces bits à 0 passe les blocs concernés en lecture seule et cette opération est irréversible.
Tous les blocs sont par défaut accessibles en lecture et écriture et le tag ne contient aucun
mécanisme de contrôle d'accès. Toutefois les blocs 5 et 6 sont spéciaux, ce sont des compteurs
de décrémentation : ils sont initialisés avec les valeurs FF FF FF FF
et ne peuvent être mis à jour
qu'avec une valeur inférieure.
Toute la sécurité anti-fraude de ces supports repose sur trois choses :
- l'UID en lecture seule qui assure l'impossibilité de cloner un titre ;
- les compteurs de décrémentation qui s'assurent qu'on ne peut pas remettre un ticket dans son état d'origine ;
- des signatures calculées à partir de l'UID et des compteurs qui s'assurent que les données inscrites sont bien authentiques, c'est à dire encodées depuis un automate de vente disposant d'une clé secrète pour les calculer.

Structure d'un ticket
Pour rendre la lecture plus claire j'ai mis les octets de poids fort côté gauche.
0: 61 40 20 30 Système et distribution
1: 25 09 16 24 Distribution
2: 04 1F 6A C4 Distribution
3: FA A2 28 08 Usage A
4: CC F3 83 E9 Usage A
5: FE 00 00 09 Compteur 1
6: FF FF FF FC Compteur 2
7: 00 05 E0 FA Usage A
8: C0 00 18 43 Usage A
9: C4 12 B6 AF Usage A
10: FA A1 08 08 Usage B
11: C2 F3 83 EB Usage B
12: 00 00 A0 00 Usage B
13: 00 00 18 43 Usage B
14: C4 00 CE 3F Usage B
15: 91 B5 61 9F Signature
OTP: FF FF FF FF Bloc OTP
UID 0: D0 02 33 7F Première partie de l'UID
UID 1: 6F 2D 0E 69 Deuxième partie de l'UID
- Les blocs 0, 1, 2 contiennent des données de distribution qui ne changent pas après l'émission d'un ticket ;
- les blocs 3, 4, 7, 8, 9 et 10 - 14 contiennent respectivement les usages A et B, chaque usage contient une signature de 16 bits à sa fin, ils stockent alternativement les informations du dernier et avant-dernier passages ;
- le bloc 5 contient un compteur ici divisé en deux parties : les trois octets de poids faible sont
décrémentés de 1 à chaque validation d'un nouveau titre (pas en correspondance), l'octet de poids
fort est décrémenté à chaque rechargement. Ce dump est un « carnet de 10 » qui a été
chargé une seule fois et dont un seul titre a été utilisé. Lors de l'achat la valeur du bloc 5 était
FE 00 00 0A
. - le bloc 6 est décrémenté lors de chaque utilisation (chargement ou validation), dans cet exemple il a été décrémenté lors de l'achat puis deux fois puisque validé deux fois (dont une fois en correspondance), le dernier bit de ce bloc permet de définir quel usage va être écrasé à la prochaine validation ;
- le bloc 15 contient une signature pour les données de distribution, cette signature ne change que lors d'un rachat de titres (elle est calculée entre autres en fonction de l'octet de poids fort du bloc 5).
Le choix de réserver 5 blocs par usage en sacrifiant la taille des données de distribution est un choix de Tisséo, d'autres partitionnements sont possibles et décrits dans la norme.
Extraction et interprétation des données
Norme Intertic
Ces titres de transports sont encodés à la norme Intertic, plus précisément :
- la norme Afnor NF P99-410 qui définit les règles Intertic en elle-mêmes et leur principe de formatage ;
- la norme Afnor NF P99-416 qui définit comment encoder les règles Intertic sur différents supports en fonction de leurs limites, notamment les tickets ST25TB.
Intertic est la version « allégée » d'Intercode, une autre norme prévue pour les cartes d'abonnement qui possèdent plus de mémoire. Il existe aussi Interbob pour normaliser les échanges entre les systèmes informatiques et Transmodel pour les bases de données.
Le formatage des données sur un titre peut être complexe parce qu'Intertic est très flexible et peut s'adapter aux règles de différents réseaux de transports. Les données sont inscrites sur chaque titre de manière séquentielle mais entre ces données il arrive que des champs spéciaux indiquent quelles données optionnelles sont présentes ou absentes. Heureusement la quantité de mémoire très réduite du titre rechargeable impose une certaine simplicité. En réalité pour les titres rechargeables Tisséo, à une exception près, les données sont situées à des emplacements fixes ce qui les rend faciles à décoder.
Système et distribution
Pour extraire les informations générales d'un titre, il faut lier les blocs 1, 2 et 0 dans cet ordre puis les découper :
Bloc 1 : 001001010000 100100010110 001001 00
A B C D...
Bloc 2 : 000001 0000011111011010 1011000100
E F...
Bloc 0 : 0110 00010 10 00000001 0000 0001 10000
G H I J K L
Lettre | Taille (bits) | Signification | Valeur |
---|---|---|---|
A | 12 | Code pays | 0x250 (France) |
B | 12 | Autorité organisatrice | 0x916 (Tisséo) |
C | 6 | Version de contrat | 9 |
D | 8 | Fournisseur de contrat | 1 |
E | 16 | Tarif de contrat | 2010 |
F | 14 | Date de fin de contrat | 2028-01-05 |
G | 5 | Définit le formatage | |
H | 2 | Définit le formatage | |
I | 8 | Exploitant | 1 |
J | 4 | Vide | |
K | 4 | ID du jeu de clés (système) | 1 |
L | 5 | ID de partition (système) | 0x10 |
- Les dates sont codées sur 14 bits, en nombre de jours depuis le 1er janvier 1997 ;
- les données de « contrat » sont liées aux normes Intercode et Intertic, je ne connais pas en détail leur signification ;
- la date de fin de contrat sert ensuite pour calculer les dates des usages ;
- le tarif définit probablement le type de titres chargé, je suppose que
2010
sert à indiquer qu'il s'agit d'un carnet de 10, c'est cohérent avec le comportement de l'automate qui m'autorise uniquement un rechargement par 10 sur mon ticket non vide - c'est très bête comme limitation mais mon intuition est que le tarif permet aussi de faire la différence entre un titre unitaire et un titre aéroport ou journée par exemple (on ne peut charger qu'un seul type de titre) ; - les signatures sont calculées au moins en fonction de l'UID, de l'ID de partition, de l'ID du jeu de clés et du tarif ;
- l'ID de partition sert à décrire le type de partitionnement utilisé parmi plusieurs possibles
décrits dans la norme Intertic, le partitionnement
0x10
permet d'avoir des usages de 5 blocs.


Normalement je suis supposée pouvoir charger le type de titre que je souhaite une fois que la carte est vide, j'ai tendance à penser que le bug qui m'empêche de recharger ma première carte est causé par le type de titre non disponible à l'achat (déplacement SAV, tarif 2060).
Usages
Découpage du usage B pour l'exemple, c'est pareil pour le A :
Bloc 10 : 1111101010 10000100001 00000001 000
A B C D...
Bloc 11 : 11 00001 01111 001110 00001111101011
E F G H
Bloc 12 : 00 0000000000000010 10 000000000000
I J K L...
Bloc 13 : 00 00000000000000 0001 10000100001 1
M N O P...
Bloc 14 : 11 0001 000000 000 0 1100111000111111
Q R S Signature
Lettre | Taille | Signification | Valeur usage B | Valeur usage A |
---|---|---|---|---|
A | 10 | Date de validation | 2025-04-16 | 2025-04-16 |
B | 11 | Heure de validation | 17:37 | 18:13 |
C | 8 | Exploitant | 1 | 1 |
D | 5 | Nature | 3 (métro) | 3 (métro) |
E | 5 | Type de validation | 1 (validation entrée) | 6 (correspondance) |
F | 5 | Définit le formatage | ||
G | 6 | Définit le formatage | ||
H | 14 | ID de ligne | 1003 (ligne C) | 1001 (ligne A) |
I | 2 | Direction | - | - |
J | 16 | Code station (optionnel) | 2 | 23 |
K | 2 | Définit le formatage | ||
L | 14 | ID de ligne précédente | - | 1003 (ligne C) |
M | 14 | ID de ligne encore précédente | - | - |
N | 4 | Définit le formatage | ||
O | 11 | Heure de 1e validation | 17:37 | 17:37 |
P | 3 | Définit le formatage | ||
Q | 4 | Nombre de passagers | 1 | 1 |
R | 6 | Nombre de trajets | 1 | 0 |
S | 3 | Nombre de correspondances | 1 | 0 |
- La date est codée en nombre de jours à soustraire depuis la date de fin de contrat de la partie distribution ;
- différents codes nature existent, notamment 1 pour le bus, 3 pour le métro, 4 pour le tramway, 5 pour le train, etc. Sur Tisséo, la ligne C et le câble Téléo sont indiqués comme des métros ;
- le champ direction est rempli pour le bus et le tramway : 1 pour l'aller et 2 pour le retour ;
- le champ du code station n'est indiqué que pour une validation sur un portique, c'est le seul champ qui est présent de façon optionnelle sur ces tickets ; lors d'une validation dans un bus ou un tramway il n'est simplement pas présent (pas juste vide), ce qui décale toute la lecture des champs suivants !
- lorsqu'un ticket vierge est édité par un automate de vente, l'usage A est signé et contient une heure de validation et de première validation à
23h59
, cette valeur n'est pas inscrite pour les tickets vendus à bord ou au service client - d'ailleurs reconnaissables par une couleur différente ou un tarif spécial SAV.
Identifiants internes
Le tableau suivant référence les différents codes station que j'ai repérés au fil du temps. Le réseau de Toulouse est un des rares (le seul ?) ou on valide en correspondance dans le métro, ce qui permet au valideur de toujours connaître la ligne empruntée. Je ne vois pas de lien avec les données GTFS disponibles en open data, ce sont des identifiants internes. J'ai l'impression que les stations sont par ordre alphabétique ?
ID | Ligne | Station |
---|---|---|
1 | C | Arènes (gare) |
2 | C | Colomiers (gare) |
23 | A | Arènes |
29 | A | Capitole |
31 | A | Esquirol |
33 | A | Jean Jaurès |
36 | A | Marengo SNCF |
43 | A | Saint-Cyprien |
46 | B | Canal du Midi |
51 | B | Jeanne d'Arc |
53 | B | Jean Jaurès (depuis la A) |
54 | B | Jean Jaurès (depuis l'extérieur) |
55 | B | La Vache |
57 | B | Palais de justice |
59 | B | Ramonville |
64 | B | Saint-Michel |
65 | B | Trois Cocus |
83 | Téléo | Université Paul Sabatier |
Ce tableau référence l'ID de chaque ligne dans le système, là encore ça ne correspond pas aux données GTFS. Pour les autobus, c'est juste le numéro de la ligne de bus.
ID | Nom commercial de la ligne |
---|---|
24 | Bus aéroport |
835 | Bus relai métro B |
1001 | Métro A |
1002 | Métro B |
1003 | Train Arènes-Colomiers (ex ligne C) |
1005 | Tramway T1 |
1007 | Câble Téléo |
Ce tableau référence les ID correspondant aux tarifs, c'est à dire les différents types de tickets qu'on peut charger sur un titre rechargeable. La liste n'est pas exhaustive, il m'en manque quelques uns.
ID | Type de billet |
---|---|
2005 | 1 déplacement |
2007 | 1 déplacement dernière minute (à bord) |
2010 | 10 déplacements |
2020 | 2 déplacements |
2060 | 1 déplacement (SAV) |
2090 | 1 déplacement aéroport (à bord) |
Sécurité du support
La décrémentation du bloc 5 de manière similaire à un automate est possible mais invalide la signature du bloc 15 et rend le titre illégitime.
On pourrait penser que changer l'horodatage du dernier usage pour simuler une correspondance pourrait éviter la décrémentation d'un titre lors de la validation mais chaque usage est protégé par sa propre signature.
Impossible aussi d'escalader de tarif par exemple en achetant un titre « 1 déplacement » et en le transformant en ticket aéroport parce que son authenticité est protégée par la signature du bloc 15.
Il reste l'hypothèse de remonter dans le temps en réinitialisant le ticket dans un état passé. Ça nécessite d'incrémenter le compteur 5, c'est normalement matériellement impossible mais les tags ST25TB sont sensibles aux attaques par arrachage, ça a fait l'objet de recherches depuis plusieurs années et les réseaux de transport sont supposés être informés.
Dans leur note technique TN1530 de mai 2024 STMicroelectronics conseille :
Follow the recommendations provided in section Antitearing protection of “Best practices for security and privacy with ST25 NFC/ RFID Tags” (AN5493)
Ça nous mène à la note d'application AN5493 qui nous met déjà dans l'ambiance dès la page 2 :
ST25 NFC /RFID Tags devices are not certified products that can be used in entry level security applications. In case of needs of a more secure solution as in the transportation domain, for example, STMicroelectronics can propose the CD21 product portfolio using the certified secure ST31 IC family and compliant with all Calypso requirements.
Après nous avoir présenté les diverses fonctionnalités de différents tags de la série ST25 le même document - mis à jour en mai 2025 - nous précise dans la section 5.2.3.4 Antitearing protection (p. 28) :
It may not be sufficient in the applications that are often faced daily human threats and risks such as fraud. Certain enhanced attacks to cause an impact on the security automatic mechanism and to exploit vulnerability may occur and need to be thwarted by combining technical controls since ST25 series are not considered as security certified products and may not resist to advanced attacks.
C'est quand même abusé pour un système vendu spécifiquement pour les transports.
Enfin toujours dans la même section le document suggère quelques stratégies : utiliser des checksums pour assurer l'intégrité des données (oui mais quel rapport avec mon potiron ?), utiliser des signatures (déjà fait) ou faire des vérifications côté serveur, ce qui est probablement la seule stratégie viable à terme mais techniquement très compliqué pour un réseau de transports.
Pour conclure, Tisséo utilise la norme Intertic de manière optimale pour assurer le respect des contraintes de la tarification dans la mémoire réduite du ST25TB512 mais cette technologie n'offre pas un niveau de sécurité adapté à l'usage qui en est fait. Les cartes Pastel utilisées pour les abonnements permettant déjà de charger des titres unitaires, en vendre des non-nominatives comme fait IDFM avec le Navigo Easy ou les réseaux de transport du Québec avec la carte Opus aurait garanti un meilleur niveau de sécurité malgré un coût du support plus élevé. Les nombreux réseaux de transports en France qui utilisent le ST25TB s'exposent aux mêmes problèmes.
Pour en apprendre plus
- Décodeur de titres rechargeables Tisséo par votre serviteuse
- Script de décodage Intertic générique, gentilkiwi
- FAQ des tickets rechargeables, Tisséo
- Near Field Chaos, outil pour lire et écrire librement sur les tags ST25TB !
- [PDF] La NORMALISATION un outil pour l’INTEROPÉRABILITÉ de la billettique dans les transports publics, Certu (PDF, 2008)
- [PDF] Datasheet du ST25TB512-AT
- [PDF] ST25TB series NFC tags for fun in French public transports, gentilkiwi
- [PDF] Tears for fears, breaking an RFID counter