MokaByte Numero  38  - Febbraio  2000
Ambiente JavaCard
Informazioni avanzate

I parte

 

di 
Roberto Fabbrica
Prosegue il viaggio nel mondo embedded 
controllato da Java

L’ambiente JavaCard, pur fornendo tutte le funzionalità necessarie all’utilizzo di codice Java e alla gestione degli oggetti, presenta alcune importanti differenze e limitazioni rispetto agli ambienti Java per workstation, dovute principalmente alla destinazione d’uso delle SmartCard e alle ridotte risorse disponibili. La conoscenza di queste caratteristiche è necessaria, sia al progettista sia allo sviluppatore, per realizzare qualsiasi applicazione JavaCard

Introduzione
Per comprendere correttamente gli argomenti trattati in quest’articolo, è consigliabile conoscere i concetti behase relativi all’ambiente JavaCard. Questi possono essere facilmente acquisiti leggendo l’articolo [JC], scritto dallo stesso autore di quest’articolo, e pubblicato da MokaByte nel numero 37 (gennaio 2000).
In questa prima parte dell’articolo saranno inizialmente analizzate le limitazioni caratteristiche dell’ambiente JavaCard rispetto agli ambienti Java per workstation. La maggior parte di queste è dovuta alla limitatezza delle risorse disponibili. Si noti che le risorse minime per il funzionamento dell’ambiente JavaCard sono un processore a 8 bit con frequenza di pochi megahertz, 24 KByte di ROM, 16 KByte di EEPROM e 500 Byte di RAM.
Nel proseguo, sono introdotte e discusse alcune estensioni dell’ambiente JavaCard. Queste estensioni sono necessarie per fornire tutte quelle funzionalità richieste ad una SmartCard, che non hanno corrispondenza sulle workstation. Una delle più importanti è la gestione nativa della persistenza degli oggetti e delle transazioni. La persistenza permette di garantire la sopravvivenza degli oggetti quando la SmartCard non è alimentata ed è solitamente implementata, in maniera trasparente, utilizzando come supporto di memorizzazione la memoria EEPROM. Altrettanto importante è la gestione delle transazioni, che garantiscono la consistenza degli oggetti e permettono di realizzare l’atomicità, cioè l’esecuzione di operazioni complesse, composte di diverse istruzioni elementari, come un’unica istruzione. Si noti che queste funzionalità hanno lo scopo preciso di mantenere la consistenza dei dati custoditi sulla JavaCard, anche quando questa non è alimentata o quando accadono interruzioni di alimentazione, fenomeno tutt’altro che raro con questi dispositivi.
 
 
 

Informazioni avanzate sull'ambiente Java Card   - parte I
Le limitazioni più importanti dell’ambiente JavaCard, rispetto agli ambienti Java per workstation, sono quelle elencate di seguito:
 

  • Ogni classe può contenere al massimo 127 metodi di istanza o di classe;
  • Ogni classe può contenere al massimo 255 Byte di variabili istanza;
  • Non sono presenti meccanismi per la gestione dei Thread, esiste un solo flusso di esecuzione per una sola JavaCard Applet alla volta;
  • Non è garantita la presenza di un Garbage Collector (questo implica anche che il metodo finalize( ) delle classi potrebbe non essere chiamato). Ciò significa che ogni oggetto creato occupa una quantità di memoria non più utilizzabile in alcun modo: questo obbliga, come sarà spiegato successivamente, ad utilizzare particolari attenzioni nella creazione e nell’utilizzo degli oggetti;
  • Gli unici tipi di dati garantiti sono boolean, byte e short. Non è garantita la gestione del tipo di dato int e non è prevista la gestione dei tipi di dato char, float, double e long, ne tantomeno il tipo di dato String;
  • Gli array sono disponibili solo come array unidimensionali;
  • Le classi per la gestione dell’I/O, dell’AWT e delle funzionalità di rete non sono supportate. La comunicazione fra entità esterna e JavaCard avviene tramite l’utilizzo di APDU (Application Protocol Data Unit), le cui caratteristiche sono discusse in questo stesso articolo;
  • Le eccezioni non prevedono stringhe che le descrivano, al loro posto è possibile utilizzare due byte che rappresentano una Status Word codificata;
  • Non sono disponibili le funzionalità di clonazione di oggetti;
  • Non è possibile caricare nuove classi a tempo di esecuzione (caricamento dinamico delle classi). Per ulteriori informazioni su questo punto si consulti l’articolo menzionato nell’introduzione.
  • Notare che la mancanza della gestione del tipo di dato integer obbliga all’esplicito utilizzo del casting verso i tipi di dati supportati, byte o short, per tutte quelle operazioni che ritornano risultati di tipo intero (quali la quasi totalità delle operazioni aritmetiche). Per ulteriori informazioni sui temi trattati finora si rimanda a [Snm97a].
    In verità, alcune delle JavaCard disponibili sul mercato hanno risorse hardware superiori e funzionalità aggiuntive a disposizione dello sviluppatore e del progettista: processori a 32 bit, maggiori disponibilità di ROM, EEPROM e RAM, gestione del tipo integer, presenza di un Garbage Collector, ecc. Se però s’intende realizzare bytecode portabile su diverse implementazioni JavaCard, e quindi usufruire della proprietà “write once, run anywhere” propria del linguaggio Java, conviene attenersi alle indicazioni della specifica API JavaCard. L’unica eccezione riguarda il tipo di dati integer: tale tipo di dati è, in effetti, praticamente presente su tutte le JavaCard attualmente in commercio, quindi risulta in pratica più che accettabile progettare e sviluppare software che utilizza questa caratteristica non standard.
    La più immediata delle differenze fra ambiente JavaCard e gli ambienti Java per workstation è costituita dalla sostituzione del concetto di applicazione con il concetto di JavaCard Applet. Una JavaCard Applet rappresenta una singola applicazione eseguibile in ambiente JavaCard. Notare che una JavaCard può tipicamente contenere diverse JavaCard Applet, selezionabili alternativamente, per permettere la coesistenza di diversi servizi sullo stesso dispositivo. Le caratteristiche principali delle JavaCard Applet sono quelle elencate di seguito:
     
    • Solo una JavaCard Applet alla volta è in esecuzione, per passare ad un’altra JavaCard Applet è necessario deselezionare la JavaCard Applet corrente e selezionarne un’altra;
    • Ogni JavaCard Applet è univocamente identificata da un AID (Application IDentifier), che gli viene assegnato in fase di caricamento nella JavaCard, ed eredita dalla classe javacard.System.Applet;
    • Le JavaCard Applet sono passive, nel senso che non iniziano mai una comunicazione ma possono solo rispondere a richieste provenienti dal CAD (Card Acceptance Device, il lettore dove viene inserita la JavaCard);
    • Le JavaCard Applet sono isolate di default una dall’altra, ma possono condividere risorse fra loro o fra alcune di loro dichiarandolo esplicitamente tramite il metodo System.share().


    Per comprendere correttamente il concetto di JavaCard Applet, può essere utile l’analisi della procedura di utilizzo dei servizi, presenti sul dispositivo, da parte dell’applicazione esterna di controllo.
     

    1. Il CAD riceve un comando formattato all’interno di un APDU (un apposito oggetto utilizzato per la comunicazione fra JavaCard e applicazione esterna di controllo) e lo invia alla JavaCard;
    2. Il JCRE interpreta l’header dell’APDU e riconosce se si tratta di un comando per la JavaCard Applet corrente o di una selezione per una nuova JavaCard Applet;
    3. Se si tratta di un comando, l’APDU viene passato alla JavaCard Applet corrente. Se si tratta di una selezione, il Framework procede alla selezione della nuova JavaCard Applet e passa a quest’ultima, una volta attivata, l’APDU stesso;
    4. Una volta che la JavaCard Applet ha terminato le elaborazioni richieste, restituisce un risultato formato da eventuali dati di risposta e da una SW (Status Word composta di due byte) che indica se l’operazione è terminata con successo (valore esadecimale 0x9000) oppure no (codice del tipo di eccezione riscontrata). Questo risultato viene infine restituito al CAD dal JCRE.


    Notare che il JCRE si occupa anche dell’invocazione dei metodi nativi corrispondenti a funzioni di utilità generale, quale la cancellazione di tutto il contenuto della JavaCard, chiamato in gergo Master Erasing. In questi casi il comando APDU ricevuto non viene inviato ad una JavaCard Applet e non serve a selezionare una nuova JavaCard Applet (come descritto precedentemente nel punto due), ma viene utilizzato per richiamare il metodo nativo richiesto con gli eventuali parametri specificati.
    Ritornando alle differenze fra ambiente JavaCard e ambienti Java per workstation, esistono anche funzionalità native presenti nell’ambiente JavaCard che non trovano corrispondenza negli altri ambienti. Una delle più importanti è la gestione nativa della persistenza, che implica una separazione netta in due classi degli oggetti utilizzabili all’interno dell’ambiente JavaCard:
     

    • Oggetti persistenti: si tratta di oggetti che hanno durata illimitata e che rimangono immutati anche quando l’Applet JavaCard corrispondente non è attiva o la JavaCard non è collegata ad un CAD (cioè è non alimentata). Un oggetto può essere reso persistente fondamentalmente in due modi: chiamando il metodo Applet.register( ) o facendolo riferire da un oggetto persistente (quali una JavaCard Applet);
    • Oggetti non persistenti o transienti: si tratta di oggetti che hanno durata limitata nel tempo, solitamente collegata al tempo di attività dell’Applet JavaCard corrispondente o al tempo di collegamento della JavaCard al CAD (cioè al periodo in cui la JavaCard è alimentata). Un oggetto può essere reso esplicitamente Transiente chiamando il metodo System.makeTransient( ).


    Notare che esiste una gran differenza di prestazioni nella gestione di oggetti persistenti e oggetti transienti, soprattutto per quanto riguarda operazioni di scrittura: operazioni che coinvolgono oggetti persistenti sono più lente di qualche ordine di grandezza rispetto ad operazioni che coinvolgono oggetti transienti. Questo è dovuto al diverso supporto di memoria utilizzato per contenere oggetti persistenti (EEPROM, memoria statica riscrivibile elettronicamente) ed oggetti transienti (RAM, memoria dinamica).
    Un’altra funzionalità non supportata a livello nativo negli ambienti Java per workstation è la gestione nativa delle transazioni. Questa funzionalità permette di eseguire in maniera atomica un insieme di operazioni indipendenti. Le transazioni sono realizzabili utilizzando i metodi esposti di seguito:
     

    • System.beginTransaction( ): dichiara l’inizio di una transazione;
    • System.commitTransaction( ): dichiara la fine di una transazione e provvede a rendere definitive le modifiche apportate;
    • System.abortTransaction( ): dichiara la fine di una transazione e provvede a scartare le modifiche apportate;
    Naturalmente, se la JavaCard è forzatamente estratta dal CAD o cade l’alimentazione durante una transazione, le modifiche effettuate sono perse e, al successivo riavvio, è ripristinata la situazione relativamente all’inizio della transazione. Notare inoltre che il meccanismo delle transazioni implementato nelle JavaCard non permette la realizzazione di transazioni innestate e che gli oggetti transienti non possono partecipare alle transazioni.
    Le transazioni sono realizzate dal JCRE utilizzando un buffer, mantenuto all’interno della memoria RAM, per la memorizzazione le informazioni necessarie al ripristino della situazione iniziale. Questo implica, vista l’esigua disponibilità di memoria RAM, che le transazioni realizzabili dal programmatore all’interno di Applet JavaCard devono essere di dimensioni sufficientemente ridotte o tenere in debita considerazione lo spazio ancora disponibile al momento di aggiungere ulteriori operazioni all’interno di una transazione. Per avere informazioni sulla situazione del buffer di memoria destinato a contenere le informazioni per il ripristino esistono appositi metodi. Notare che, oltre alla transazioni, il programmatore può disporre di una serie di metodi per cui è già garantita l’esecuzione atomica, quali per esempio quelli dedicati alla copia di array. Rimangono comunque disponibili anche versioni non atomiche degli stessi metodi, utilizzabili quando il programmatore lo ritiene conveniente o quando è necessario a causa di vincoli di memoria disponibile.
    Un’altra importante differenza dell’ambiente JavaCard rispetto agli ambienti Java per workstation è relativa alla gestione delle eccezioni. La differenza immediatamente visibile è che le eccezioni dell’ambiente JavaCard non contengono la stringa esplicativa dell’eccezione, bensì due byte destinati a contenere una SW (Status Word) che codifica la spiegazione dell’eccezione. E’ comunque possibile creare eccezioni proprie, trattare le eccezioni con blocchi di codice try catch, rilanciare eccezioni tramite throw e creare metodi che dichiarano la possibilità di lanciare eccezioni tramite throws. Purtroppo, viste le ridotte disponibilità di memoria, è fortemente sconsigliato, anche se possibile, procedere alla creazione dinamica di eccezioni al bisogno, come si fa quando si utilizza un ambiente Java per workstation. Bisogna piuttosto procedere creando eccezioni personalizzate, ereditando dalla classe java.lang.Exception del Framework JavaCard, ed utilizzando il metodo statico throwIt( ). Questo metodo permette di modificare la status word associata e di eseguire il lancio dell’eccezione, avendo a disposizione tale eccezione in ogni punto della JavaCard Applet e senza la necessità di utilizzare memoria per la creazione dell’oggetto. Il modo di utilizzo delle eccezioni appena descritto è esattamente quello utilizzato per le eccezioni predefinite nel Framework.
    Un esempio della differenza fra i due modi di utilizzo delle eccezioni in ambiente JavaCard è il seguente: supponiamo di voler utilizzare l’eccezione MyException, ottenuta ereditando dalla classe Exception del Framework JavaCard. La modalità tipica degli ambienti Java per workstation richiederebbe una riga di codice come la seguente:
     
    throw new MyException(sw);


    dove sw è un valore short a 16 bit che specifica meglio l’eccezione. Purtroppo questa invocazione provoca l’allocazione di memoria per la creazione di un nuovo oggetto MyException, memoria non più recuperabile in alcun modo se non tramite un garbage collector, entità non sempre disponibile in ambiente JavaCard. Il corretto utilizzo dell’eccezione MyException può essere realizzato tramite una riga di codice come la seguente:
     

    MyException.throwIt(sw);


    dove sw è sempre una valore short a 16 bit che descrive l’eccezione. Questa invocazione, a differenza della precedente, non utilizza memoria per la creazione dell’oggetto, pur sortendo in tutto per tutto lo stesso effetto.
    Una delle eccezione predefinite nell’API JavaCard più utilizzate è l’eccezione ISOException. Questa eccezione, quando viene lanciata, obbliga il JCRE ad inviare la status word dell’eccezione corrente al CAD come SW di risposta. L’eccezione ISOException può anche essere lanciata dall’utente, che può personalizzare la status word tramite lo stesso metodo utilizzato per lanciarla, il metodo throwIt().
     
     
     

    Bibiografia 
    Per approfondire i concetti trattati in questo articolo si consiglia la lettura dei seguenti documenti, tutti disponibili nel sito web di SUN dedicato a Java (www.javasoft.com):
     

    [JC] "JavaCard: tecnologia Java per le smart card" di R. Fabbrica -  MokaByte 37  - Gennaio 2000 (www.mokabyte.it/2000/01)
    [Snm97a]: SUN Microsystems, “Java Card 2.0 Language Subset and Virtual Machine Specification – Revision 1.0 Final”, 1997, www.javasoft.com
    [Snm97b]: SUN MicroSystems, “Java Card 2.0 Programming Concepts – Revision 1.0 Final”, 1997, www.javasoft.com
    [Snm97c]: SUN MicroSystems, “Java Card 2.0 Application Programming Interfaces – Revision 1.0 Final”, 1997, www.javasoft.com
    [Snm98]: SUN MicroSystems, “Java Card Applet Developer’s Guide – Revision 1.12”, 1998, www.javasoft.com
     



    Roberto Fabbrica nato a Ravenna il 28 dicembre 1972, è laureato in Ingegneria Informatica alla Facoltà di Ingegneria dell’Università di Bologna dal 1999. Attualmente lavora come libero professionista, fornendo consulenze come analista programmatore in Visual Basic ad una software house bolognese e collaborando al progetto e all’implementazione di applicazioni Java presso il DEIS (Dipartimento di Elettronica, Informatica e Sistemistica) della Facoltà di Ingegneria dell’Università di Bologna, sede di Cesena.
     
    Chi volesse mettersi in contatto con la redazione può farlo scrivendo a mokainfo@mokabyte.it