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.
-
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;
-
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;
-
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;
-
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.
|