Simulation d'Applications
Java Card

Carine Courbis
Rapport de stage du DEA d'Informatique de Lyon
INRIA Sophia-Antipolis
Action OASIS
2004, route des Lucioles, BP 93
06 902 Sophia-Antipolis Cedex
Carine.Courbis@sophia,inria.fr
Juillet 1998
Stage financé par Dyade (G.I.E. Bull/INRIA)

Responsables: Dr. Isabelle Attali & Pr Denis Caromel


Résumé

Le développement de nouveaux composants plus puissants va permettre l'installation de plusieurs applications sur une même carte à puce. Elles pourront contenir des données aussi disparates que de la monnaie électronique, des crédits téléphoniques ou des informations médicales utiles en cas d'accident. L'apparition d'un nouveau langage de haut niveau adapté aux standards 7816 de la carte à puce, Java Card (sous-ensemble de Java), offre de nouvelles possibilités en matière de portabilité et de sécurité. Nous étudions la sémantique du langage Java Card afin de construire un environnement de développement et de vérification de programmes Java Card.


Mots-clés: Java Card, applets, carte à puce intelligente, sémantique, environnement graphique

Sommaire

1. Introduction *

2. Etat de l'art *

  • 3. Construction d'un environnement Java Card *

  • 4. Discussion et travaux futurs *

  • 5. Conclusion *

     

    Remerciements

    Je tiens à remercier Isabelle Attali et Denis Caromel pour m'avoir accueillie pour ce stage. Leurs conseils, encouragements, aide, patience me furent d'une grande utilité durant ces quatre mois.

    Je voudrais aussi remercier toutes les personnes du projet CROAP pour m'avoir si rapidement adoptée et pour m'avoir aidée à "vaincre Centaur ".

    Je tiens à remercier plus particulièrement :

    • Yves Bertot, responsable du projet CROAP, et Laurence Rideau pour leur l'intérêt qu'ils ont porté à mon travail et pour leur aide.

    • Marjorie Russo pour son inestimable aide, sa patience, ses encouragements. Elle fut toujours disponible pour répondre à mes trop nombreuses questions sur sa sémantique et sur Typol.

    • Valérie Pascual, ma personnelle " hot-line Centaur ". Que de fois je l'ai dérangée ! Elle fut d'une grande aide lors de la spécification de la syntaxe avec Metal et du pretty printer en Ppml. De plus, elle est capable de donner une explication à tous les messages d'erreur de Centaur (même lors de la rencontre du quatrième type avec des heaps OVNI et des PPML UFO).

    • Francis Montagnac, le magicien d'Unix. Le monde Unix n'a plus de secret pour lui. C'est aussi un champion pour trouver les bugs dans le protocole de communication entre Typol et Centaur.

    Je tiens à remercier aussi Claire Loiseaux et Dominique Bolignano de Dyade (GIE Bull/INRIA) pour avoir répondu si gentiment à mes questions sur Java Card.

     

    1. Introduction
    2. L'informatique a pris une place prépondérante dans notre vie de tous les jours durant ces vingt dernières années. Elle est utilisée partout : pour la gestion des capteurs dans nos voitures, pour aider les chirurgiens à opérer, pour les télécommunications, pour retirer de l'argent d'un guichet automatique avec une carte à puce, etc. Cette formidable évolution fut possible grâce au développement des technologies (aux progrès fulgurants de l'électronique) qui a permis, en autre, la réduction de la taille des éléments de stockage d'information (les transistors) dans les composants. Cette miniaturisation a permis d'augmenter la taille de la mémoire d'un composant et donc de passer d'une programmation proche de la machine (assembleur) à une programmation haut niveau avec, par exemple, des langages à objet (Java, C++, etc.).

       

      Ce phénomène est en train d'apparaître dans le domaine très fermé des cartes à puces intelligentes (les smart cards) très populaires en Europe et au Japon mais encore peu développées aux Etats-Unis où on préfère utiliser les cartes magnétiques. Mais tout cela est en train de changer grâce à l'apparition de nouvelles puces plus puissantes permettant d'installer plusieurs applications sur le même composant. Par exemple, elles pourront contenir des données aussi disparates que de la monnaie électronique, des crédits téléphoniques, des informations médicales utiles en cas d'accident, etc. Il y a un intérêt grandissant pour les cartes à puce (la demande augmente de 40% par an [DRE98]). En effet, ces possibilités d'authentification et de stockage d'information permettent des accès sécurisés à l'Internet. Gemplus (un fabriquant de cartes à puce) estime qu'il y en aurait environ 3 000 millions à la fin de 2001.

       

      Présentation des cartes à puce

      Il y a deux sortes de cartes à puce : la carte à mémoire servant seulement au stockage d'information utilisant la logique pour accéder à l'information (ex : une carte de téléphone avec 50 crédits) et la carte à puce intelligente (celle qui nous intéresse dans ce rapport) qui est un mini-ordinateur. Elle contient un micro-contrôleur 8 bits (les plus connus étant les composants 6805 de Motorola et le 8051 d'Intel) qui a des propriétés de sécurité. Sa mémoire est composée de RAM où sont stockées les données temporaires (i.e. ces données sont perdues lors de la perte de courant), d'EEPROM où sont stockées les données sur le possesseur de la carte (ex : le numéro de compte bancaire, la clé privée d'encryption, etc.) et de la ROM où résident les programmes de la carte.

      Le protocole ISO 7816 définit un ensemble de standards internationaux pour les cartes à circuit intégré avec contacts que doivent suivre les programmeurs d'application pour que les opérations soient possibles sur n'importe quel terminal (le lecteur de carte ou CAD : Card Acceptance Device) dans le monde entier qui fournit le pouvoir électrique à la carte. La structure de communication utilisée entre le CAD et la carte est un format standardisé de donnée : une APDU (Application Protocol Data Unit) qui est un message de commande du CAD vers la carte ou un message de réponse de la carte vers le CAD (cf. Figure 1 ).

      Figure 1 Format des APDUs

      Cla indique la classe de la commande, ins le code de l'instruction à faire, p1 et p2 sont les paramètres de la commande, lc est la longueur en octet des données, le est le nombre maximum des octets attendus pour la donnés de l'APDU de réponse. Sw1 et sw2 indiquent comment s'est passée la commande (le mot d'état).

       

      En mars 1996, un petit groupe d'ingénieurs de Schumberger au Texas ont souhaité simplifier la programmation des cartes à puces tout en préservant la sécurité des données [GUT98]. Presque immédiatement, ils ont analysé que ce problème avait déjà été posé par le chargement de code sur le World Wide Web. La solution avait été Java. Malheureusement vu la taille de la mémoire disponible sur une carte à puce seulement un sous-ensemble de Java pouvait être utilisé (la taille de la JVM (Java Virtual Machine) et du système de runtime ne devant pas dépasser 12 kø). C'est ainsi qu'est né Java Card, le premier langage à objets adapté aux cartes à puces. Schlumberger et Gemplus sont les co-fondateurs du JavaCard Forum qui recommande des spécifications à JavaSoft (la division de Sun à qui appartient Java Card) en vue d'obtenir un standardisation du langage et il s'occupe aussi de la promotion des APIs (Application Programming Interface) Java Card pour qu'il devienne la plate-forme standard de développement des applications pour les smart cards

       

      Java présente trois avantages majeurs pour la programmation des cartes à puce :

      • C'est un langage haut niveau, à objets permettant l'encapsulation des données et la réutilisation de code. Ainsi les programmeurs n'auront plus besoin d'apprendre les langages assembleur 6805 ou 8051 pour réaliser une application pour une carte. La mise au point de programmes sera plus rapide et les temps de développement seront beaucoup plus courts. De plus, c'est un langage sûr : pas de manipulation de pointeurs, pas d'allocation de mémoire, ... C'est un langage fortement typé, avec des niveaux de contrôle d'accès (public, protected ou private) de ses membres (classe, méthodes, variables).
      • Il est exécutable sur n'importe quel système d'exploitation (Motorola ou Intel) car son code est pré-compilé en byte-code qui est exécuté par la machine virtuelle Java (JVM). Donc cette portabilité permettra d'écrire du code qui fonctionnera sur n'importe quel micro-processeur de cartes à puces (" Write Once, Run Anywhere ").
      • Il possède un modèle de sécurité qui permet à plusieurs applications de coexister en sécurité sur la même carte.

       

      Présentation de Java Card

      C'est un sous-ensemble de Java adapté aux cartes à puce. Sa syntaxe est légèrement moins riche à cause de la faible capacité mémoire d'une puce. C'est pourquoi, les utilisations de tableaux multidimensionnels, des types float, double ou long ou des modifiers transient et volatile ne sont pas autorisées [SUN1] ainsi que les threads. Ses APIs sont basées sur les standards 7816 et ont été adoptées par 95% des constructeurs de cartes à puce. Les applications Java Card sont appelées des applets [SUN2], identifiées uniquement par leur AID (Application IDentifier) isolées entre elles par des applets firewall. Elles possèdent 5 méthodes : la méthode install() qui permet de créer l'applet, la methode register() qui permet d'enregistrer l'applet au JCRE (Java Card Runtime Environment), les méthodes select() et deselect() qui permettent la sélection ou la desélection d'une applet et la méthode process() qui traite les APDUs de commande. Plusieurs applets pourront cohabiter sur une carte même si elles appartiennent à des fabriquants différents. Les applets d'une carte sont gérées par le JCRE. Les minima requis pour faire tourner un programme Java Card sont 16kø de ROM, 8kø d'EEPROM et 256ø de RAM [CHE98].

      Java Card, bien qu'il soit un sous-ensemble de Java, possède des mécanismes spéciaux liés aux spécificités des cartes à puce. Ainsi il a 2 types d'objets : les objets transients (i.e. stockés en RAM qui perdront leurs valeurs lors du retrait de la carte, d'une coupure de courant, de la desélection de l'applet ou après chaque transaction) et les objets persistents stockés en EEPROM. Java Card permet de garantir l'atomicité des transactions. Il permet aussi de partager des objets entre applets sous certaines conditions. Les lignes de sécurité sont implémentées par la machine virtuelle.

      Le gros avantage de Java Card est la possibilité de charger dynamiquement, n'importe quand, une nouvelle applet sur la carte. Cela signifie que le code des applets pourra être remis à jour.

       

      Présentation du problème

      L'usage de Java Card au lieu des assembleurs propriétaires utilisés actuellement pour coder des applications pour les cartes à puce permettra de réduire énormément le temps de réalisation. De plus il est possible d'utiliser les environnements de développement existants pour Java (par exemple Symantec Visual Café, Microsoft Visual J++, etc.) pour Java Card.

      Malheureusement les erreurs de programmation ne sont pas exclues même avec un langage de haut niveau. C'est pourquoi les développeurs ont besoin d'outils pour les aider à vérifier le comportement de leurs applets Java Card avant leur mise en mémoire dans une carte à puce.

      L'idéal serait de pouvoir vérifier le fonctionnement des applets sur une station de travail. Lors de cette simulation, on pourrait visualiser les objets, les attributs contenus par la carte, les APDUs transmises. Le but de notre étude est donc de construire un environnement basé sur la sémantique pour tester le fonctionnement d'applications Java Card.

      Nous nous appuyons sur deux applets Java Card : PurseFr et FrequentFlyer qui permettent d'ajouter et retirer respectivement des francs et des miles.

       

      La section 2 présente un état de l'art sur la sémantique et les outils existants. La section 3 explique la construction de notre environnement de simulation d'application. La section 4 présente les travaux futurs.


    3. Etat de l'art
    4. Il existe de nombreux travaux liés à la sémantique et à la sécurité de Java et plus récemment de Java Card. C'est pourquoi cette étude commence par une présentation des sémantiques formelles de Java (dont celle utilisée pour notre travail). De plus, il est nécessaire pour juger de la pertinence de notre travail de connaître les outils existants adaptés à Java Card i.e. le vérificateur statique (appelé checker) fourni par Sun et les outils commerciaux Odyssey-Lab de Bull, GemXpresso RAD de Gemplus et Cyberflex de Schlumberger.

      1. Les Sémantiques Formelles de Java
        1. Travaux Formels sur Java
        2. Un important domaine de recherche est la preuve de la sûreté des types en Java. Drossopoulou, Einsenbach [DRO98] et Syme [SYM97] spécifient la sémantique de différents sous-ensembles Java séquentiels (incluants les types primitifs, les classes, l'héritage, les variables et méthodes d'instances, les interfaces, la surcharge des variables d'instance, la création d'objet Java, les tableaux et les exceptions) pour prouver que l'exécution d'un programme préserve les types. En rapport avec leurs travaux, Nipkow et Oheimb [NIP98] définissent et prouvent des propriétés sur leur sous-ensemble Java Light avec le prouveur de théorème Isabelle/HOL.

          Börger et Schulte [BOR98a], pour leur part, définissent une sémantique dynamique par empilement de différents sous-ensembles Java.

        3. Travaux Formels sur le Byte Code
        4. Une autre approche pour prouver la sûreté des types est de travailler au niveau du byte code de la JVM. Qian [QIA98] a défini une sémantique pour un sous-ensemble d'instructions de la JVM. Les comportements de ces instructions dans la mémoire lors du runtime sont décrites comme des transitions d'états. Son but comme Börger et Schulte [BOR98b] est de prouver le bon fonctionnement du byte code lors de son exécution.

          Joachim Posegga et Harald Vogt [POS98] définissent, pour leur part, un système à état fini pour la vérification de byte code pour Java Card. Eva Rose [ROS97] a elle aussi spécifiée un sous ensemble Java, JavaOb, assez restreint pour modéliser Java Card. Puis elle a définit une traduction de JavaOb vers un sous-ensemble de la JVM, JVMOb dont la sémantique statique correspond au vérificateur Java de byte code. Ainsi elle a obtenu une formalisation de son sous-ensemble et peut proposer une procédure de vérification de la sûreté du byte code.

        5. Une sémantique naturelle pour Java
        6. Cette sémantique [RUS97], [ATT97] (spécifiée par Marjorie Russo, doctorante du projet CROAP) est celle sur laquelle notre sémantique Java Card est basée (en modifiant le moins possible sa syntaxe et sa sémantique pour les adapter à Java Card).

          Pour cette description formelle du langage, une sémantique big-step est adoptée pour l'héritage et les caractéristiques objets alors que le multithreading est spécifié par une sémantique small-step (ou opérationnelle). L'ensemble de cette spécification représente environ 400 règles Typol.

          Cette sémantique naturelle spécifie un large sous-ensemble Java et est exécutable dans le système Centaur.

           

          Le système Centaur est un outil de génération d'environnements de langages de programmation développé par le projet CROAP. Il permet de spécifier formellement des langages aussi différents qu'Eiffel, Esterel ou ASN1.

          Pour spécifier un langage avec cet outil, il faut tout d'abord définir sa syntaxe concrète et sa syntaxe abstraite à l'aide du langage Metal. Cette première étape permet de générer un analyseur syntaxique (parseur). Son rôle est d'analyser syntaxiquement un programme et de créer son arbre de syntaxe abstraite. La deuxième étape est la spécification de règles d'affichage en Ppml pour générer un pretty-printer. Enfin la dernière étape est la spécification de la sémantique du langage à l'aide de règles Typol. De cette sémantique, on pourra constituer des outils d'aide à la programmation (comme par exemple un interprète ou vérificateur de type). La Figure 2 permet d'avoir une vue simplifiée du système.

          Figure 2 Environnement de programmation construit avec Centaur

          La Syntaxe

          Le langage Metal permet de spécifier la grammaire de langages de programmation. Il se décompose en 2 parties :

          • la syntaxe concrète utilisée pour analyser syntaxiquement un programme.
          • la syntaxe abstraite utilisée pour construire l'arbre abstrait du programme.

          Quand un fichier (contenant un programme du langage décrit) est lu sous Centaur, le parseur associé au langage utilisé dans le fichier construit l'arbre de syntaxe abstraite (cf. Figure 3 ) représentant le programme. Cet arbre sert de base pour faire de l'édition structurée, de la sémantique statique ou dynamique. En cas d'erreur syntaxique, l'arbre n'est pas généré et un message d'erreur apparaît. Le Tableau 1 présente des exemples de spécification de syntaxe concrète et abstraite écrits en Metal.

           

          Exemple de syntaxe concrète en Metal:

          <VariableDeclaratorId>::=<Identifier> ; <Identifier>

          <VariableDeclaratorId>::=<Identifier>"[""]"; array(<Identifier>) (1)

          <Identifier> : := %ID ; identifier-atom(%ID) ;

           

          Exemple de syntaxe abstraite en Metal:

          VariableDeclaratorId ::= array Identifier ;

          Identifier ::= identifier class ;

          Identifier -> implemented as IDENTIFIER ;

          Array -> Identifier ; (2)

          Class -> implemented as SINGLETON ;

          Explications

          Le non terminal VariableDeclaratorId est utilisé lors de la déclaration d'une variable. Il décrit les variables de type tableau.

          Int varT[] ;

          Dans ce cas varT est analysé par la règle (1) de VariableDeclaratorId. L'arbre construit aura pour l'opérateur array et pour feuille un identifier i.e. dans ce cas varT En effet, comme cette règle s'applique, la fonction de construction d'arbre array(<Identifier>) est appelée. Elle correspond à un opérateur de syntaxe abstraite (2).

          Tableau 1 Exemples de spécification de syntaxe écrits en Metal

          Il faut spécifier un pretty-printer en langage Ppml pour qu'un arbre de syntaxe abstraite du langage soit visible dans une représentation textuelle (cf. Figure 4).

           

          i
          Figure 3 Arbre de syntaxe abstraite du célèbre programme HelloWorld.java (présentation arborescente)

          i
          Figure 4
          Arbre de syntaxe abstraite du célèbre programme HelloWorld.java (présentation textuelle Java)

           

          La Sémantique

          Les sémantiques statiques et dynamiques doivent être spécifiées en Typol, qui est un langage dédié à la sémantique naturelle. C'est un langage déclaratif, logique, typé, manipulant la syntaxe abstraite du langage. Il fournit des mécanismes de filtrage, d'unification et de retour arrière.

          Les règles peuvent être regroupées dans des sets

          Le Tableau 2 présente un exemple de règle écrite en Typol.

           

          A partir des spécifications décrites en Typol, Centaur permet de générer des outils sémantiques comme un vérificateur de type ou un interprète pour le langage. Le résultat d'un vérificateur de type sur un programme donné est une liste d'erreurs avec une référence de l'emplacement de chaque erreur dans l'arbre de syntaxe abstraite (ainsi l'erreur pourra être mise en exergue dans le programme). Au cours de l'interprétation d'un programme des structures sémantiques (contenant des informations utiles sur le comportement du programme comme par exemple la liste des objets créés par le programme ou bien la liste des variables statiques) sont construites. Donc un résultat possible de l'interprète pourrait être une de ces structures sémantiques (ex : la liste des objets et leurs attributs à la fin de l'interprétation). Ces résultats sont montrés dans des fenêtres spécifiques (textuelles ou graphiques). De plus, Centaur permet d'animer l'exécution d'un programme en utilisant des mises à jour incrémentales des fenêtres contenant les résultats.

           

          Exemple de règle en Typol :

          Jugdement Object_L, O_Id |- Object ;

           

          find_Apdu_attribute(identifier "commandApdu" |- ObjL1, AttrL1 -> AttrL2)

          --------------------------------------------------------------------------

          ObjL1, OthId1 |- object(_, identifier "CAD", AttrL1, _, _, _, _);

          Do

          Update_apduL(APDU(OthId1, AttrL2) -> ApduL) &

          emit_tree("APDU", ApduL) ;

          Explications

          Cette règle d'inférence ne s'applique que si l'objet a comme nom de classe CAD (mécanisme utilisé : le filtrage) et si la prémisse find_Apdu_attribute n'échoue pas. Cette prémisse recherche la référence de l'objet APDU, contenu par l'objet de type CAD donné, dans la liste d'attributs (AttrL1). Puis elle recherche cet objet dans la liste des objets du programme (ObjL1) pour en extraire la liste d'attributs pour une APDU de commande (AttrL2).

          Donc si la règle réussit, une mise à jour de la liste des APDUs est faite (update_apduL), suivie par son affichage dans la fenêtre APDU (cf. Figure 10).

          Tableau 2 Exemple d'une règle Typol

      2. Le checker Java Card
      3. Pour compiler des programmes Java Card (cf Figure 5), il faut utiliser le compilateur de Java, javac, en précisant dans le classpath les librairies spécifiques à Java Card (i.e. throwable.jar, jc20api.jar, jcre.jar et jcsimulation.jar). Tout emploi de méthodes, exceptions ou classes n'appartenant pas à Java Card est détecté lors de cette phase de compilation.

        Le compilateur javac, qui a un analyseur syntaxique adapté à Java et non à Java Card, ne peut garantir que le programme compilé est bien écrit en Java Card car certaines erreurs syntaxiques ne pourront pas être détectées.

        Pour cette raison, Sun fournit en plus des librairies Java Card un utilitaire appelé checker qui permet de vérifier si les packages/applets sont écrits en Java Card et non en Java. Son rôle est de détecter l'usage de types ou de modifiers non supportés, d'opérations arithmétiques non autorisées et de classes, méthodes ou exceptions n'appartenant pas à Java Card. Cet outil travaille sur le byte code du programme (c'est pourquoi il lui est impossible de donner le numéro de ligne où se situe l'erreur).

        Figure 5 La compilation d'un programme Java Card

        Cet outil peut être utilisé de deux manières :

        • Sur un programme Java Card pour tester des programmes compilés avec les librairies Java Card pour s'assurer que le programme est légal.
        • Sur un programme Java pour connaître les modifications à apporter pour pouvoir le transformer en programme Java Card (le checker indiquera les classes, méthodes et exceptions non supportées).

      4. Les outils commerciaux adaptés à Java Card
      5. Actuellement il en existe trois: Odyssey-Lab de Bull, GemXpresso RAD de Gemplus et Cyberflex de Schlumberger. Les seules informations disponibles sont des documentations commerciales donc non objectives.

        1. Odyssey-Lab de Bull
        2. [BUL98]

          C'est un environnement de développement fournissant les outils nécessaires pour optimiser et charger les applets (compilées avec un compilateur quelconque). Odyssey I (nom de la carte) est conforme aux spécifications Java Card API 2.0 et sa JVM est l'une des plus performantes du marché selon Bull. Son composant, développé par ST (auparavant SGS Thomson), a été conçu pour les besoins des plates-formes ouvertes et pour être adapté à de nouveaux mécanismes de sécurité (accès sécurisé aux secteurs partitionnés de la mémoire permettant une séparation étanche des applications, authentification ; intégrité et confidentialité assurées lors du chargement des applets ; cryptographie). La carte Odyssey offre en standard l'application de crédit/débit CCPS de Visa et 8 kø d'EEPROM pour des applications propriétaires (ex : programmes de fidélité, porte-monnaie électronique,...).

        3. GemXpresso Rapid Applet Development (RAD) de Gemplus
        4. Il est le premier membre d'une famille de produits de carte à puce supportant les APIs Java Card 2.0 pour 8 ou 32 bits. Son kit inclut 2 cartes pour le prototypage d'applets, un lecteur de carte et un environnement de développement. Cette plate-forme de développement est basée sur les spécifications de Java Card 2.0 et sur un processeur RISC 32 bits. Selon les informations commerciales données par Gemplus [GEM98] elle permettrait d'accélérer le développement et les tests des applets pour la programmation d'un prototype ou d'un pilote car elle contiendrait un simulateur intégré. De cette façon, les tests des programmes se feraient directement sur une station de travail. Il ne serait pas nécessaire de les charger sur une carte au préalable pour cette phase de mise au point car le simulateur permettrait de représenter ce qui se passe dans la carte. Malheureusement Gemplus ne donne pas plus d'informations techniques sur ce simulateur.

        5. Cyberflex 2.0 Multi8K de Schlumberger [SLB97]
        6. Cyberflex est une carte intelligente Java (appelée J-Card) 8 bits développée par Schlumberger qui peut exécuter des applications Java Card (les Cardlets). Elle est conforme aux spécifications de Java Card 2.0. Le kit vendu pour PC comprend à peu près les mêmes éléments que celui de Gemplus mais apparemment n'a pas d'environnement de développement car il serait très facile de l'intégrer dans un environnement de développement Java existant (comme par exemple Microsoft Visual J++).

          Cet outil posséderait lui aussi un simulateur qui vérifierait le code d'une cardlet après la phase de compilation et d'édition de lien faite dans un environnement de développement Java quelconque. Pour l'utiliser, il suffirait juste de le lancer en cliquant dans le menu Tool de l'environnement utilisé, puis d'exécuter la cardlet. Il suffirait ensuite d'interagir avec la carte par l'interface graphique du simulateur. Il détecterait les erreurs fonctionnelles possibles du code avant que celui ne soit chargé sur la carte. Après cette simulation le byte code de la cardlet est modifié par MakeSolo (un outil du kit qui teste l'absence de caractéristiques non supportées et qui groupe les fichiers .class en un seul fichier) puis peut être chargé sur la carte Schlumberger par l'utilitaire LoadSolo.

           

          Ces trois outils ne sont pas spécifiques à Java Card car ils permettent la mise au point de programme Java. Leur spécificité Java Card intervient au niveau du byte code (généré dynamiquement) avec une phase de vérification puis de chargement.


    5. Construction d'un environnement Java Card
    6. Tout d'abord, on indiquera les modifications apportées aux spécifications syntaxiques Java pour les adapter à Java Card. Puis on décrira un outil de vérification statique spécifié pour détecter la présence de tableaux multidimensionnels. Puis enfin notre simulateur d'application Java Card sera présenté.

      1. Définition syntaxique de Java Card
      2. Tout le travail sur la syntaxe de Java Card repose sur le document Java Card 2.0 language subset and virtual machine specification [SUN1] écrit par Sun Microsystems. Notre rôle était de modifier ou de supprimer les types et les opérateurs adéquats de la spécification syntaxique de Java qui contenait 130 types et 209 opérateurs.

         

        Notre première action fut donc d'enlever les types de base non supportés en Java Card (i.e. char, long, double et float).

        Puis comme les threads ne sont pas supportés, le modifier synchronized (utilisé pour synchroniser l'accès à une donnée ou à une méthode lors de l'exécution multi-threadée d'un programme) fut enlevé.

        Comme aucune gestion de la mémoire (garbage collector) n'est spécifiée en Java Card, les modifiers volatile (permettant d'indiquer que cette variable est une copie d'une donnée partagée par deux threads ayant comme avantage d'être remise à jour à chaque accès) et transient (permettant d'indiquer que la valeur d'un attribut d'un objet n'est pas persistante) ont été enlevés.

        Java Card, contrairement à Java, ne possède que des tableaux unidimensionnels. C'est pourquoi des modifications ont dû être faites pour éviter la déclaration et l'usage de tableau à plus d'une dimension. Pour cela, il a fallu restreindre la spécification des types utilisables pour la déclaration d'un attribut, d'une variable locale, du type de retour d'une méthode, ou d'un cast. Malgré ces modifications, il est encore possible de faire une analyse syntaxiquement correcte d'un programme illégal en Java Card (i.e. contenant un tableau à 2 dimensions). En effet, Java lors de la déclaration d'une variable, d'un attribut ou du type de retour d'une méthode, laisse la possibilité au développeur de positionner les crochets indiquant un tableau soit à droite du nom du type, soit à droite du nom de la variable, de l'attribut ou de la méthode (après ses paramètres). C'est pourquoi, ces 3 déclarations int[] i , int i[] et int[] i[] sont syntaxiquement correctes en Java Card mais le dernier exemple n'a aucun sens pour un programme Java Card car le tableau déclaré a 2 dimensions. Ce type de vérification est donc à effectuer pendant la phase d'analyse statique (cf. partie 3.2 pour avoir plus de détails).

        Lors de cette analyse fine de la syntaxe de Java et de Java Card, nous avons détecté des erreurs dans la description syntaxique de Java qui était trop permissive par rapport à la grammaire LALR définie par Gosling [GOS96].

         

        Lors de notre spécification sémantique, nous avons eu besoin de définir une structure de résultat qui définit les APDUs transmises entre la carte et le lecteur. Cette structure, qui est une liste d'APDUs, a été rajoutée à la syntaxe abstraite. En voici sa spécification :

        APDU_L ::= apdu_l ;

        apdu_l -> APDU *... ;

        APDU ::= apdu ;

        apdu -> O_Id Pair_L ;

         

        Une APDU contient la référence de l'objet (O_Id) l'ayant transmis et une liste de paires (Pair_L). Chaque paire contient le nom de l'attribut et sa valeur (comme par exemple (" cla ", 0)). Les types O_Id et Pair_L étaient déjà définis dans la syntaxe abstraite de Java.

      3. Un exemple de vérification statique
      4. Pour débuter avec le langage Typol, nous avons décidé de spécifier un outil de sémantique statique testant que les tableaux n'ont qu'une dimension (cf. partie 3.1 pour une description de la problématique). Cette spécification permettait de se familiariser au parcours de l'arbre de syntaxe abstraite d'un programme Java Card et à la syntaxe des règles Typol.

        Le but était donc d'analyser un programme et de vérifier sa légalité en Javacard. En cas d'erreur, un message est affiché (cf. Figure 6).

         

        Les emplacements possibles d'erreur

        • Déclaration des champs (i.e. variables contenues dans une classe/interface)

        • Déclaration d'une variable locale (i.e. dans un bloc (méthode, if...)) byte[] var1[]

        • Déclaration des paramètres d'une méthode/constructeur void foo(byte[] var1[]) {...}

        • Déclaration du type de retour d'une méthode byte[] foo()[] {...}

         

         

        Nous avons ainsi pu identifier les nœuds de l'arbre abstrait à analyser i.e. fieldVariableDeclaration, methodDeclaration, localVariableDeclaration.

         

        Algorithme utilisé

        Début : noeud typeDeclarations de compilationUnit
        Pour chaque noeud fieldVariableDeclaration 
           si son fils type est arrayof(TypeName)
              Pour chaque variableDeclarator
                 si c'est un array ou un initializedVariable avec un VariableDeclaratorId qui est un array
                    msg d'erreur
        Pour chaque noeud methodDeclaration
           si ResultType est un array(TypeName) et 
           si MethodDeclarator est un arrayMethodDeclarator 
              msg d'erreur
        
           Pour chaque parameter
              Si Type = arrayof(TypeName) et VariableDeclaratorId = array
                 msg d'erreur 
        
        Pour chaque noeud constructorDeclaration
           Pour chaque parameter
              Si Type = arrayof(TypeName) et VariableDeclaratorId = array
                 msg d'erreur
         
        Pour chaque noeud localVariableDeclaration
           si Type = arrayOf(TypeName)
              Pour chaque variableDeclarator
                 si c'est un array ou un initializedVariable avec un VariableDeclaratorId qui est un array
                    msg d'erreur
        

        Ci-dessous (cf. Tableau 3) sont écrites les règles Typol nécessaires pour tester que les paramètres d'un constructeur sont corrects (i.e. ne sont pas des tableaux à plusieurs dimensions).

         

         

        program javacardCheckCompilationUnit is

        use javacard ;

        import message(string, string, string, string, path, string) from message ;

        import diff(_,_) from prolog ; ...

         

        set javacardCheckCompilationUnit is

        judgement (CompilationUnit -> Bool);

        checkTypeDeclaration_l(TypeDec -> Bool)

        ----------------

        (compilationUnit(_, _, TypeDec) -> Bool) ; // on ne teste que le noeud typeDeclarations

        end javacardCheckCompilationUnit;

         

        set checkTypeDeclaration_l is

        judgement (TypeDeclarations -> Bool);

        MainRule: // on teste recursivement tous les types déclarations

        CheckTypeDeclaration(TypeDec -> Bool1) &

        CheckTypeDeclaration_l(TypeDecL -> Bool2) & and(|- Bool1, Bool2 -> Bool3)

        ----------------

        (typeDeclarations[TypeDec.TypeDecL] -> Bool3) ;

         

        (typeDeclarations[] -> true()) ;

        end checkTypeDeclaration_l;

         

        set checkTypeDeclaration is

        judgement (TypeDeclaration -> Bool);

        ClassRule: // si le typeDeclaration est une classDeclaration, on teste sa liste de FieldDeclaration

        CheckClassField_l(FieldDecL -> Bool)

        ----------------

        (classDeclaration(_, _, _, _, FieldDecL) -> Bool) ;

        InterfaceRule: // si le typeDeclaration est une interfaceDeclaration,

        //on teste sa liste de InterfaceFieldDeclaration

        CheckInterfaceFieldDec_l(InterFieldDecL -> Bool)

        ----------------

        (interfaceDeclaration(_, _, _, InterFieldDecL) -> Bool) ;

        end checkTypeDeclaration;

         

        set checkClassField_l is

        judgement (FieldDeclarations -> Bool);

        MainRule: // on teste recursivement tous les FieldDeclaration

        CheckClassField(FieldDec -> Bool1) &

        CheckClassField_l(FieldDecL -> Bool2) & and(|- Bool1, Bool2 -> Bool3)

        ----------------

        (fieldDeclarations[FieldDec.FieldDecL] -> Bool3) ;

         

        (fieldDeclarations[] -> true()) ;

        end checkClassField_l;

         

        set checkClassField is

        judgement (javacard -> Bool);

        ConstructorRule: //si le fieldDeclaration est un constructorDeclaration

        //on teste sa liste de paramètres et son corps.

        checkParam_l(ParamL -> Bool1) & checkConstBody(ConstBody -> Bool2) &

        and(|- Bool1, Bool2 -> Bool3)

        ----------------

        (constructorDeclaration(_, constructor(_, ParamL), _, ConstBody) -> Bool3) ;

        ...

        end checkClassField;

         

        set checkParam_l is

        judgement (javacard -> Bool);

        ItParamRule: // on teste recursivement chaque paramètre

        checkParam(|- Param -> Bool1) & checkParam_l(|- ParamL -> Bool2) &

        and(|- Bool1, Bool2 -> Bool3)

        ----------------

        (parameters[Param.ParamL] -> Bool3) ;

         

        ItParamEmptyRule: (parameters[] -> true()) ;

        end checkParam_l;

         

        set checkParam is

        judgement |- javacard -> Bool;

        ArrayArrayParamRule: //erreur car le type du paramètre est un tableau à 2 dimensions

        |- parameter(_, arrayof(TName), array(Param)) -> false() ;

        do

        message("javacard", "checker", "errorDimArray", "Error", subject, "");

        ArrayParamRule: //Le type du paramètre est un tableau à 1 dimension

        |- parameter(_, arrayof(TName), Param) -> false() ;

        provided diff(Param, array(_));

        SimpleParamRule: //parametre

        |- parameter(_, TypParam, _) -> true() ;

        provided diff(TypParam, arrayof(_));

        end checkParam; ...

        Tableau 3 Spécifications de l'outil de vérification

        Figure 6 Fenêtre contenant les erreurs du programme testé

      5. Le simulateur d'applets Java Card
        1. Le modèle
        2. Ses acteurs

          Les principaux acteurs lors d'une transaction entre une carte à puce et un CAD sont :

          • l'utilisateur qui choisit quelle application et quelle opération il souhaite effectuer.
          • le CAD. Son rôle est d'attendre la commande de l'utilisateur, de la transmettre codée dans l'APDU à la carte. Puis il doit attendre la réponse de la carte (une APDU de réponse). Les deux APDUs seront affichées décodées à l'utilisateur.
          • la carte à puce qui contient le JCRE et les applications possibles (dans notre cas les applets PurseFr et FrequentFlyer). Son rôle est d'attendre une commande du CAD, de l'exécuter et de renvoyer une réponse.

           

          Nous avons décidé de simuler l'ensemble de ces acteurs pour pouvoir tester des applets Java Card sur une station de travail. Pour cela, nous avons codé en Java le comportement de chaque acteur.

           

           

          Son architecture

          Pour simplifier notre modèle dans une première approche, nous avons décidé de simuler seulement les événements synchrones i.e. les actions de l'utilisateur sur le lecteur de carte. Donc tous les événements asynchrones comme une coupure de courant ou un retrait de carte ne sont pas encore gérés par notre modèle. Mais nous en avons tenu compte lors de la phase de conception afin de faciliter leur intégration future lors de l'évolution de notre modèle.

          Chaque acteur (User, CAD, JCRE) est modélisé par un Thread Java (cf. Figure 7).


          Figure 7 Schéma du modèle adopté

          Nous avons choisi de représenter les différents acteurs par des Threads pour représenter au mieux la réalité et pour permettre la gestion future des coupures de courant ou des retraits de carte. En effet, dans la réalité un utilisateur est indépendant du lecteur de carte. Il peut à tout moment retirer sa carte ou taper sa commande sur le lecteur. Donc le lecteur est un processus en attente d'actions de l'utilisateur. Le JCRE de la carte est aussi un processus en attente d'APDU de commande.

          Nous avons aussi choisi de représenter les applets de la carte par des threads. En effet, il est envisagé dans le futur d'introduire du multithreading sur une carte à puce. De plus, cela nous permettait de séparer les contextes d'exécution des objets contenus sur la carte et donc d'avoir une meilleure visualisation de ce qui se passe sur la carte.

          Le JCRE doit être le plus générique possible pour pouvoir référencer n'importe quel type d'applet contenue par la carte.

           

          Son fonctionnement

          Nous avons choisi un mode de fonctionnement maître/esclave permettant d'avoir un déroulement séquentiel. C'est pourquoi un seul Thread est actif à un instant donné comme nous le montre la Figure 8.


          Figure 8
          Déroulement séquentiel de l'exécution de notre programme

          Description des différentes phases

          Tous les threads (User, CAD, JCRE) sont créés, démarrés, puis suspendus (méthode invoquée : suspend()) en attente d'être réveillés (méthode resume()).

          1. Le thread User est réveillé. Il simule un utilisateur i.e. envoie une action au CAD. Le thread User réveille le Thread CAD pour lui envoyer une action ou une demande d'installation d'applet et se suspend (i.e. attend la réponse à son action).
          2. Le thread CAD fabrique une APDU de commande en fonction de l'action demandée, l'envoie au JCRE, le réveille et se suspend (l'exécution d'une action peut prendre 2 transactions si l'applet indiquée n'est pas déjà sélectionnée car il faudra au préalable la sélectionner sur la carte avant de pouvoir exécuter l'action).
          3. Quand le thread JCRE se réveille, il regarde si l'APDU de commande demande une sélection ou non. Si c'est une sélection, il desélectionne l'applet courante et demande la sélection de l'applet indiquée (l'APDU contient l'AID d'une applet). Sinon il réveille l'applet courante et se suspend. Dans les 2 cas, il est suspendu, en attente de la réponse de l'applet.
          4. Quand l'applet est réveillée, la méthode indiquée (i.e. soit process(), select() ou deselect()) est exécutée. Lors de l'exécution de la méthode process() une APDU de réponse est créé. L'applet réveille le Thread JCRE et se suspend.
          5. Le JCRE envoie l'APDU de réponse attendue par le CAD. Il réveille le CAD et se suspend.
          6. Le CAD affiche l'APDU, réveille le Thread User et se suspend (i.e. attente d'une nouvelle action).

           

          Son Implémentation

          Le programme se compose de 10 classes :

          • la classe Root (thread principal) sert uniquement à créer les Threads User, JCRE et CAD.
          • La classe User simule un utilisateur exécutant des actions sur un lecteur de carte.
          • La classe CAD. Son rôle est de traduire les actions de l'utilisateur soit en APDU de commande pour la carte soit en installant une nouvelle application sur la carte.
          • La classe JCRE. Cette classe gère les applets contenues sur la carte. Elle sert d'interface entre le lecteur de carte et les applets.
          • La classe PurseFR. Cette applet permet la gestion d'un porte-monnaie électronique.
          • La classe FrequentFlyer. Cette applet gère les miles bonus octroyés par une compagnie aérienne lors de l'achat d'un billet d'avion
          • La classe APDU. Cette classe permet une représentation simplifiée des APDUs.
          • La classe Applet, l'ancêtre de PurseFr et FrequentFlyer. Elle contient la méthode run() des threads qui permet de rendre la gestion des threads invisible pour ces sous-classes.
          • La classe Object utile pour l'interprète (elle ne contient que la déclaration du constructeur).
          • La classe Thread aussi utile pour l'interprète (elle contient juste la déclaration du constructeur et de la méthode start()).

           

          Vue la longueur de notre programme (environ 500 lignes), seulement les parties les plus intéressantes des classes User (Tableau 4), CAD (Tableau 5), JCRE (Tableau 6), Applet (Tableau 7) et PurseFr (Tableau 8) sont présentées et commentées. L'annexe A contient l'ensemble du programme.

           

          class User extends Thread {...

          Public void run() {

          Cad.installAppli(PurseFr.AID, 1000);

          cad.sendAction(PurseFr.AID, PurseFr.PURSE_FR_WITHDRAW, 100);

          cad.installAppli(FrequentFlyer.AID, 0);

          cad.sendAction(FrequentFlyer.AID, FrequentFlyer.GAIN_MILES, 5000);}...}

          Commentaires

          Cette classe simule l'utilisateur. Sa première action est de faire installer par sa banque l'application PurseFr avec comme montant initial 1000 francs sur sa carte. Puis il se sert de sa carte pour payer son essence (retrait de 100 francs). Il décide ensuite de faire installer l'application Frequent Flyer par sa compagnie aérienne car il souhaite pouvoir conserver les miles de son voyage en Suède offerts (gain de 5000 miles). Ainsi il pourra, un jour, avoir un voyage gratuit

          Chaque appel à la fonction sendAction envoie au CAD l'AID de l'applet sur laquelle l'opération indiquée doit être faite et attend sa réponse.

          La fonction installAppli permet de simuler le chargement dynamique du code d'une applet sur une carte déjà en service.

          Tableau 4 Une partie de la classe User

           

           

          Class CAD extends Thread {...

          Public void run() {

          While (true) {

          sendActionToCard();

          if (action == SELECT) {

          if (apdu.sw1 == JCRE.OK) {

          currentAID = AID;

          apdu.setCommandApdu(JCRE.PROCESS_CLA, ins, NOTHING, commandData);

          sendActionToCard();

          }

          else

          currentAID = JCRE.NO_SELECTED_APPLET;

          }

          user.resume();

          suspend();}...}

          Commentaires

          Cette classe simule le lecteur de carte qui traduit l'action demandée en APDU de commande standardisée en fonction des informations données lors de l'appel de la méthode sendAction().

          Tableau 5 Une partie de la classe CAD

           

           

           

           

          Class JCRE extends Thread {...

          Public void run() {

          while (true) {

          /*// Select APDU*/

          if (apdu.cla == SELECT_CLA && apdu.ins == SELECT_INS) {

          if (selectedAppliAID != NO_SELECTED_APPLET) {

          sendCommandToAppletAndWaitResponse(DESELECT, selectedAppliAID);

          selectedAppliAID = NO_SELECTED_APPLET;

          }

          sendCommandToAppletAndWaitResponse(SELECT, apdu.p1);

          if (apdu.sw1 == OK)

          selectedAppliAID = apdu.p1;

          } else { /*// Process APDU*/

          if (selectedAppliAID != NO_SELECTED_APPLET)

          sendCommandToAppletAndWaitResponse(PROCESS, selectedAppliAID);

          else

          apdu.sw1 = IMPOSSIBLE;

          }

          cad.apdu.copyResponseApdu(apdu);

          cad.resume();

          suspend(); /*Wait a CAD action*/ }...}

          Commentaires

          Cette classe simule le runtime de la carte. Son rôle est d'attendre une commande du CAD pour sélectionner, desélectionner les applets de la carte ou leur envoyer les commandes de l'utilisateur contenues dans les APDUs.

          Tableau 6 Une partie de la classe JCRE

           

          class Applet extends Thread {.../*//Java Card Applet*/

          public void run() {

          byte commandCode;

          boolean selectOk;

          while (true) {

          commandCode = JCRE.self.commandCode;

          if (commandCode == JCRE.SELECT) {

          selectOk = select();

          if (selectOk == true)

          JCRE.self.apdu.sw1 = JCRE.OK;

          Else

          JCRE.self.apdu.sw1 = JCRE.IMPOSSIBLE;

          } else

          if (commandCode == JCRE.DESELECT)

          deselect();

          else

          if (commandCode == JCRE.PROCESS)

          process(JCRE.self.apdu);

          JCRE.self.resume();

          Suspend(); }...}...}

          Commentaires

          Cette classe décrit les Applets Java Card de notre modèle où chaque applet est un Thread. Donc chaque fois que l'on fait un resume() (pour réveiller le thread) sur une de nos applets, on exécute ce run() générique qui appelle la méthode appropriée de l'applet (i.e. soit process(), deselect() ou select()) et qui s'occupe de la gestion des Threads.

          Tableau 7 Une partie de la classe Applet

           

           

          class PurseFr extends Applet {

          protected final static byte AID = 126;

          /*//Possible Instructions*/

          protected final static byte PURSE_FR_DEPOSIT = 1;

          protected final static byte PURSE_FR_WITHDRAW = 2;

          protected final static byte PURSE_FR_BALANCE_CHECK = 3;

          ...

          public static PurseFr install(APDU apdu) {

          return new PurseFr(apdu);}

           

          public void process(APDU apdu) {

          if (apdu.ins == PURSE_FR_DEPOSIT)

          purseDeposit(apdu);

          else

          if (apdu.ins == PURSE_FR_WITHDRAW)

          purseWithdraw(apdu);

          else

          if (apdu.ins == PURSE_FR_BALANCE_CHECK)

          purseBalanceCheck(apdu);}...

          Commentaires

          Voici l'exemple d'une applet Java Card avec ses méthodes process() et install() ainsi que sa liste d'actions possibles (PURSE_FR_DEPOSIT, ...). Chaque fois d'un développeur d'applet voudra tester une applet, il n'aura qu'à insérer son code Java Card, sans le modifier, dans le programme de notre modèle comme pour cette applet PurseFr. Puis il devra modifier légèrement la méthode run() de User pour installer son applet sur la carte et pour exécuter des actions. Il n'aura pas à s'occuper de la gestion des threads.

          Tableau 8 Une partie de la classe PurseFr

        3. Modifications de la sémantique
        4. Nous avons modifié la sémantique Java, en particulier, sur l'entrelacement des threads. En effet, une sémantique small-step classique, après chaque pas d'instruction élémentaire, recherche un autre thread à exécuter (simulation de la concurrence). Le modèle décrit permet d'avancer dans l'exécution d'un thread jusqu'à sa mort ou sa suspension comme nous le montre la spécification dans le set exec_activity du Tableau 9 (les modifications ont été mises en gras dans ce set. Le set treat_next_inst_if_not_suspended a été créé spécialement pour la sémantique Java Card ainsi que le set send_apdu qui sert à communiquer avec l'environnement.

           

           

          set exec_activity is

          --C : Deals with the execution of the thread activity.

          --C : The first reference 'OThId1' is the current "executing" thread

          --C : identifier. The second one 'ObjId1' is the current object identifier.

          judgement Object_L, C_Var_L, O_Id, O_Id |- Activity -> Object_L, C_Var_L;

          judgement Object_L, C_Var_L, O_Id, O_Id |- Clr :> Object_L, C_Var_L, T_Status;

          judgement Object_L, C_Var_L, O_Id, O_Id, Clr

          |- Inst -> Object_L, C_Var_L, Env, Clr, Comp_Exp, T_Status;

          ...

          Dead_thread:

          send_object_struct(ObjL1, ClVarL1) &

          update_thread_status(ObjL1, ClVarL1, OThId1, dead() -> ObjL1_1) &

          update_activity_in_object_l(ObjL1_1, OThId1,

          clr(OThId1, Mode, MethName, env(pair_l[], locs[]), inst_l[]), ClVarL1

          -> ObjL1_2)

          ----------------

          ObjL1, ClVarL1, OThId1, ObjId1

          |- clr(OThId1, Mode, MethName, env(ParL, LocL), inst_l[]) :> ObjL1_2,

          ClVarL1, dead() ;

          Do

          send_dead(ObjL1_2, OThId1) & delete_a_given_thread_from_stack(OThId1);

           

          General_rule:

          delete_a_given_thread_from_stack(OThId1) &

          ObjL1, ClVarL1, OThId1, ObjId1, clr(OThId1, Mode, MethName, Env1,

          inst_l[Inst.InstL])|- Inst ->

          ObjL1_1, ClVarL1_1, Env1_1, Clr2, Val, ThStatus1 &

          Update_activity_in_object_l(ObjL1_1, OThId1, Clr2, ClVarL1_1 -> ObjL1_2)

          & update_thread_status(ObjL1_2, ClVarL1_1, OThId1, ThStatus1 -> ObjL1_3)

          & send_object_struct(ObjL1_3, ClVarL1_1) &

          treat_next_inst_if_not_suspended(ObjL1_3, ClVarL1_1, OthId1, ObjId1

          |- ThStatus1, Clr2 -> ObjL1_4, ClVarL1_2, ThStatus2)

          ----------------

          ObjL1, ClVarL1, OThId1, ObjId1

          |- clr(OThId1, Mode, MethName, Env1, inst_l[Inst.InstL]) :>

          ObjL1_4,ClVarL1_2, ThStatus2 ; ...

          end exec_activity;

           

          set treat_next_inst_if_not_suspended is

          judgement

          Object_L, C_Var_L, O_Id, O_Id

          |- T_Status, Clr -> Object_L, C_Var_L, T_Status;

           

          Not_suspended_thread:

          send_current(ObjL1, OThId1) &

          exec_activity(ObjL1, ClVarL1, OThId1, ObjId1 |- Clr

          :> ObjL1_1, ClVarL1_1, ThStatus1_1)

          ----------------

          ObjL1, ClVarL1, OThId1, ObjId1

          |- ThStatus1, Clr -> ObjL1_1, ClVarL1_1, ThStatus1_1 ;

          provided not_eq(ThStatus1, suspended(_));

           

          Suspended_thread:

          send_apdu(ObjL, OThId, Clr)

          ----------------

          ObjL, ClVarL, OThId, ObjId

          |- suspended(ThStatus), Clr -> ObjL, ClVarL, suspended(ThStatus) ;

          end treat_next_inst_if_not_suspended;

          Tableau 9 Spécification sémantique des threads de notre modèle

        5. Résultats obtenus lors de cette simulation
        6. Lors du déroulement de l'interprétation de notre programme, certaines structures sémantiques sont affichées incrémentalement pour faciliter la mise au point des applets Java Card.

           

          Fenêtres communes Java/ Java Card

          L'environnement Java offre quatre fenêtres de résultats dont une (cf. Figure 9) qui montre l'évolution des états (suspended, runnable, etc.) des Threads par l'utilisation de couleurs, les dépendances des objets créés par le programme, les variables locales et les attributs des objets (cf. l'objet PurseFr).


          Figure 9
          Fenêtre graphique contenant tous les objets de notre modèle

          Fenêtres spécifiques Java Card

          Pour mettre au point une application Java Card, il est utile de visualiser les APDUs transmises entre le CAD et la carte (cf. Figure 10). Ainsi on peut espionner la ligne de communication entre ces deux entités et afficher les informations transitant sur cette ligne (i.e. les APDUs de commande du CAD vers le JCRE et des APDUs de réponse entre le JCRE et le CAD). Cette même information est affichée " décodée " dans une autre fenêtre (cf. Figure 11). simulant l'écran LCD (Liquid Cristal Display) du CAD pour être comprise. De cette manière, notre outil permet aux développeurs d'applets de mettre au point des applets Java Card ainsi que l'interface du CAD.

           

          Il a fallu spécifier comment notre structure d'APDUs devait être construite dans la sémantique de notre interprète et à quels moments ces informations devaient être affichées dans les fenêtres de résultat

          Pour pouvoir afficher cette même structure de deux manières différentes (i.e. selon si c'est l'écran LCD du CAD ou bien l'espion réseau), il fallait écrire deux pretty-printers différents.


          Figure 10
          L'espion réseau montrant les APDUs échangées entre la carte et le lecteur de carte


          Figure 11
          Ecran LCD du CAD (APDUs décodées pour l'utilisateur)

    7. Discussion et travaux futurs
      1. Le modèle
      2. Notre modèle n'inclut pas toutes les possibilités de Java Card. Pour le rendre plus réaliste, il faudrait que les applets puissent utiliser des tableaux de bytes, les nombres hexadécimaux beaucoup employés pour la programmation des cartes et plus tard les APIs (Application Programming Interface) de Java Card 2.0.

        Il faudrait aussi que modèle gère l'authentification grâce au PIN (Personal Identifier Number) en début de communication entre le lecteur et la carte pour s'assurer de l'identité de la personne employant la carte.

        Les retraits de carte ou coupures de courant devront également être simulés avec sauvegarde du contexte. Ces deux événements asynchrones auront comme conséquence de cesser toute activité sur la carte. Lors de la remise du courant, toutes les opérations atomiques interrompues seront annulées.

        Le JCRE devrait stocker, pour chaque applet contenue sur la carte, l'état en plus de l'AID et de la référence. Les différents états possibles sont : none, loaded, registred, selectable.

      3. La sémantique
        1. Spécifications communes Java/ Java Card
        2. Actuellement la sémantique de Java n'est pas encore totalement spécifiée. Le mécanisme d'exception est en cours de spécification par Marjorie Russo. Il faudra juste l'adapter à Java Card (i.e. enlever les exceptions n'appartenant pas à Java Card et rajouter celles propres à Java Card).

          De plus, les nombres hexadécimaux ainsi que certaines opérations binaires comme le décalage droit (>>), gauche (<<), droit non signé (>>>), le modulo (%), le xor bit à bit (^), le et bit à bit (&) ou le ou bit à bit (|) n'ont pas été spécifiées. La plupart de ces opérations sont nécessaires pour travailler avec des nombres en hexadécimal (ex. pour faire des masques) utilisés intensément par les développeurs d'applications en assembleur pour les cartes.

          Il faudra aussi faire migrer la sémantique Java de JDK 1.0 vers JDK 1.1 pour pouvoir traiter les inner classes et interfaces.

          De plus, il faudra aussi être capable de spécifier le mécanisme de package qui permet à une application d'être modulaire multi-fichiers.

        3. Spécifications Java Card
        4. La première chose à faire serait de spécifier les comportements du JCRE, CAD et de la classe Applet en Typol. Cela permettrait de réduire la taille de notre programme à quelques lignes seulement et donc d'améliorer les performances de l'interprétation. Ainsi la gestion des threads serait complètement cachée.

          Il faudrait pouvoir spécifier toutes les classes et les méthodes de Java Card. Cette solution paraît acceptable au vue du faible nombre de classes en Java Card (51 classes dont 18 classes d'exception pour 5 packages).

          Dans un sens Java Card peut être vu comme un sous-ensemble de Java, mais pourtant il possède des mécanismes qui lui sont propres et qui sont liés aux spécificités des cartes à puce.

          Par exemple, notre sémantique devra tenir compte des objets transients. Pour cela, il faudra définir une nouvelle structure : une liste d'objets transients contenant également le moment où la remise aux valeurs par défaut doit être faite. Cette liste sera consultée et mise à jour après chaque transaction, desélection, simulation de coupure de courant ou de retrait de carte.

          Notre sémantique doit pouvoir intégrer le mécanisme de transactions atomiques. La solution serait de faire lors de la rencontre de l'appel système beginTransaction() (méthode indiquant le début d'une transaction atomique) une copie de la liste des objets persistents. Ainsi on pourrait restaurer les valeurs modifiées en cas de simulation de retrait de carte ou de coupure de courant dans une transaction atomique ou en cas d'appel de la méthode abortTransaction() (pour annuler la transaction).

          La sémantique devrait enfin définir le partage d'objets entre applets (caractéristique absente des outils commerciaux).

      4. Les Performances
      5. L'interprétation de notre programme (environ 500 lignes) grâce à la sémantique modifiée de Java montre que des programmes assez gros peuvent être traités sur station de travail. Pour donner un ordre d'idée de la vitesse de notre interprète nous pouvons dire que l'interprétation se fait en 7 mn sous un DEC PWS 500 à 500 MegaHertz, avec 256 Mø de mémoire vive et 600 Mø de swap sur disque dur. Ces performances sont acceptables en présence de l'animation de l'exécution. En effet le développeur doit pouvoir suivre le déroulement de l'exécution.

      6. L'Interface
      7. Actuellement le comportement du l'utilisateur est décrit dans le programme et est donc figé. En offrant une fenêtre d'entrée au CAD, nous permettrons à l'utilisateur de choisir l'applet et l'opération à exécuter. De cette manière, l'utilisateur ne sera plus simulé. Cette fenêtre aura l'apparence d'un vrai lecteur de carte avec des boutons et un écran LCD. Le problème sera bien sûr d'avoir un CAD générique i.e. indépendant des applications de la carte. Pour cela, il faudra une phase d'initialisation entre le CAD et la carte pour donner les AIDs et les opérations possibles des applets contenues par la carte. Actuellement les fabricants de carte et de terminaux sont en train de réfléchir à ce problème. En effet, jusqu'à présent comme nos cartes étaient mono-applications, les CADs connaissaient les opérations qui pouvaient être réalisées sur cette application. Mais maintenant avec l'arrivée des cartes multi-applications, les CADs devront devenir " intelligents ".

        D'autres fenêtres d'interfaces pourraient être créées pour visualiser le fonctionnement du JCRE et objets de type File (très couramment utilisés dans la programmation des cartes à puces).

    8. Conclusion
    9. Nous avons présenté nos travaux sur le langage Java Card: nous avons tout d'abord étudié le langage, les standards liés aux cartes à puces, puis modélisé les applications Java Card en y incluant tous les acteurs, et pas seulement ce qui se passait effectivement sur la carte.

      Nous avons modifié une sémantique formelle de Java existante afin de prendre en compte ce modèle, en particulier le fait qu'un seul thread est actif à un instant donné. Enfin, cette modélisation ainsi que la sémantique ainsi décrite permettent de comprendre le comportement des applications Java Card. L'environnement de développement construit pour Java Card facilite la mise au point des applets Java Card grâce à la visualisation des acteurs du modèle et des transactions entre terminal et carte.

      Notre objectif est, dans un premier temps, de compléter la description formelle du langage (et du modèle), puis de proposer d'autres outils pour permettre la mise au point. Enfin, nous souhaitons étudier et formaliser des propriétés liées à la sécurité qui, une fois vérifiées, pourront garantir le bon fonctionnement d'une application Java Card.

      A terme, nous pourrons ainsi disposer d'un environnement complet de développement et vérification d'applications Java Card.



    Annexe A: Programme utilisé pour faire notre simulation d'applets Java Card



    Références

    [ATT97]

    Isabelle Attali, Denis Caromel, Marjorie Russo

    Vers une sémantique Formelle de Java

    Journées du GDR Programmation 12, 13, 14 novembre 1997 Rennes

    ftp:://ftp-sop.inria.fr:/pub/oasis/mrusso/GDR_97.ps.gz

    [BOR98a]

    E. Börger and W. Schulte

    A programmer friendly modular definition of the semantics of Java

    Formal syntax et semantics of Java. LNCS. Springer-Verlag, 1998. A paraître

    [BOR98b]

    E. Börger and W. Schulte

    Defining the Java Virtual Machine as platform for Provably correct Java compilation

    23rd International Symp. On Mathematical foundations of computer sciences

    LNCS. Springer-Verlag, 1998. A paraître

    [BUL98]

    Bull CP8

    Odyssey I: Your passport to the Java world March 1998

    http://www.bull.gr/bull/www.cp8.bull.net/news/021798a.htm
    http://www.bull.gr/bull/www.cp8.bull.net/products/javacara.htm

    [CHE98]

    Zhiqun Chen (with special contribution by Rinaldo Di Giorgio)

    Understanding Java Card 2.0 (learn the inner workings of the Java Card architecture, API, and runtime environment) JavaWorld March 1998

    http://www.javaworld.com/javaworld/jw-03-1998/jw-03-javadev.html

    [DRE98]

    Henry Dreifus, J. Thomas Monk

    Smart Cards (Chapitres 1, 2, 3, 10)

    John Wiley & Sons 1998 ISBN 0-471-15748-1

    [DRO98]

    S. Drossopoulou, S. Eisenbach

    Towards an operationnal semantics and proof of type soundness for Java

    Formals Syntax and Semantics of Java, LNCS. Springer-Verlag, 1998 A paraître

    [GEM98]

    Gemplus

    Gemplus ships GemXpresso RAD, a Java-Based Development Plateform for smart cards Gemplus presse 23/03/98

    http://www.gemplus.com/presse/1998/gemxpresso.htm

    [GOS96]

    James Gosling, Bill Joy and Guy Steele

    The Java Language Specification (Chapitre 19 LALR Grammar) Addison-Wesley 1996 http://java.sun.com/docs/books/jls/htlm/19.doc.html#52996

    [GUT97]

    Scott B. Guthery (Schumberger Electronic Transactions)

    Java Card : Internet Computing on a Smart Card IEEE Internet Computing Volume 1 Jan. Feb. 1997

    [NIP98]

    T. Nipkow and D. Von Oheimb

    Java Light is Type Safe- Definitely

    25th ACM Symp. Principles of Programming Languages, 1998

    [QIA98]

    Z Qian

    A formal specification of the Java Virtual Machine Instructions for objets, methods and subroutines

    Formal syntax et semantics of Java. LNCS. Springer-Verlag, 1998. A paraître

    [ROS97]

    Eva Rose

    Towards a Secure Bytecode Verification on a Java Card

    Workshop on security and languages, Palo Alto. Oct 97

    [SYM97]

    D. Syme

    Proving Java type soundness

    Technical report 427, University of Cambridge Computer laboratory. 1997

    [RUS97]

    Marjorie Russo

    Une sémantique formelle de Java

    Mémoire de DEA de Mathématiques Discrètes de Fondements de l'Informatique de Marseille Juin 1997

    ftp:://ftp-sop.inria.fr:/pub/oasis/mrusso/Sout_Dea.ps.gz

    [POS98]

    Joachim Posegga, Harald Vogt

    Byte Code Verification for Java Smart Cards Based on Model Checking

    5th European Symposium on Research in Computer Security (ESORICS) 1998

    Springer LNCS 1998

    [SLG97]

    Schlumberger

    Presenting ... the butterfly effect ? 1997-1998

    http://www.cyberflex.austin.et.slb.com/cyberflex

    Simulation Tool Aids Development of Cyberflex Cardlets ™ for smart card

    http://www.cyberflex.austin.et.slb.com/cyberflex/top/news/032398-m8ksim.html

    [SUN1]

    Sun Microsystems

    Java Card 2.0 Language Subset and Virtual Machine Specification Révision finale 1.0 du 13 octobre 1997 http://java.sun.com/products/javacard

    [SUN2]

    Sun Microsystems

    Java Card 2.0 Programming Concepts Révision finale 1.0 du 15 octobre 1997

    http://javaworld.com/javaworld/jw-04-1998/iButtons/JC20-Concepts.pdf

    [SUN3]

    Sun Microsystems

    Java Card Checker User's Guide Révision finale 1.5 du 20 Février 1998

    [SUN4]

    Sun Microsystems

    Java Card Application Programming Interface (package javacard.framework) Révision finale 1.0 du 13 Octobre 1997

    http://javaworld.com/javaworld/jw-04-1998/iButtons/JC20API-prtrat.pdf

     


    Carine.Courbis@sophia.inria.fr

    Since November, 24th, 1998, you are our th visitor.
    Thanks for visiting!