Modelo de Memoria Java Card

O Modelo de Memória Java Card





Smart Cards são as menores plataformas computacionais e com recursos de processamento e memória por enquanto limitadissimos e embora seja Java os riscos de "memory leaks"(vazamento de memória) são reais, isso em um PC teria pouco impacto pois um simples Reset na máquina e sua memória estaria livre novamente, bem em Java Cards esse reset não existe, por sorte "alguns" cartões implementam o Garbage Collector. 
Por conta disso sua aplicação deverá ser escrita de maneira extremamente otimizada  e cuidadosa sempre economizando recursos de memória e de processamento.
Java Cards suportam ambos tipos de Objetos, persistent e transient (temporário) porém a mecânica difere da versão Java para PCs.
Um Java Card possui três tipos de Memórias : ROM (Read Only Memory), RAM (Random Access Memory) e EEPROM (Electrically Erasable Programmable Read Only Memory).
Embora a ROM seja uma memória apenas de Leitura é a de menor custo entre as três e tem o papel de armazenar a API Java Card, API Especiais do Fabricante, dados de configuração do chip e se você tiver muito dinheiro poderá também pedir para incluir uma API só sua nessa área de memória junto ao Fabricante do Chip, ou seja essa memória é gravada no ato da fabricação do Chip e não poderá ser alterada depois.
A RAM e a EEPROM, podem ser escritas e lidas porém possuem diferenças elétricas entre si, no caso de perda de energia todo conteúdo da RAM será perdida enquanto o da EEPROM será preservada.
Ora porque não utilizar somente EEPROM? Você deve se estar perguntando, porque a escrita na EEPROM leva cerca de 1000 vezes mais tempo que a escrita em memória RAM e a escrita nessas memórias possui também um limite de vezes entre 100000 ~ 500000 ciclos de escrita.
Em contra partida o circuito elétrico de uma memória RAM costuma ser 4 vezes maior que o da memória EEPROM. Aqui se faz valer aquele velho ditado "does not exist a free lunch".
A memória pode variar bastante de Fabricante para Fabricante, eu tenho em mãos cartões com (36,72,80)Kb EEPROM / 3,3Kb RAM. Porém acredito que isso deva mudar bastante com os Java Card 3 que vem por ai, basta observar que atualmente uma simples memória SD do tamanho de uma unha alcançam notáveis 16GB ou mais de memória.
Bom após toda essa explicação acredito que tenha ficado claro que Objetos Persistentes ficam na EEPROM, enquanto os Temporários (Transient) ficam na RAM.
E você já deve ter notado o quanto criterioso e cuidadoso você terá que ser enquanto programa para Java Cards.

Objetos Persistentes
Como já foi dito, Objetos Persistentes mantém seus dados integros mesmo após o final de uma sessão ou perda de energia. Esses objetos possuem as seguinte propriedades :
  •  Objetos Peristentes são criados pelo operador new,
  • Guarda seus dados e valores entre sessões.
  • Qualquer alteração de dados ocorre de maneira atomic, ou seja no caso de se haver perda de alimentaçãou ou falha durante a atualização, os dados anteriores serão restaurados.
  • Um objeto persistente pode referenciar um campo em um objeto transiente.
  • Um campo em um objeto persistente pode referenciar um objeto transiente.
  • Se um objeto persistente não for referenciado por outros objetos, ele se torna permanentemente perdido e a área de memória só poderá ser recuperada através do Garbage Collector.
Quando um applet é instanciado, como qualquer outro objeto persistente, os dados e recursos alocados persistem indefinidamente através das sessões seguintes.

Objeto Transiente (Temporário)
Objetos Transientes possuem uma natureza temporária, ou seja dependendo de como for configurado esse objeto tem seus dados destruidos quando outra aplicação for selecionada, ou quando a sessão terminar ou quando houver perda de energia. Assim como os objetos persistentes os recursos alocados pelos objetos transientes não podem ser recuperados sem o auxilio do Garbage Collector. Seu Applet deverá criar objetos transientes apenas uma vez, ou seja uma referencia a esse objeto deverá ser criada e colocada na área persistente e essa mesma referencia deverá ser usada durante as sessões posteriores, a figura abaixo demonstra como objeto transientes são alocadas na memória.

Apenas arrays de tipos primitivos (byte, short e boolean) e objetos (Object) podem ser declarados como transiente. Os objetos transientes possuem as seguintes propriedades :
  • São criados invocando-se a API Java Card.
  • Não guarda seus valores entre sessões, os dados serão limpos para valores padrões (zero, false ou null) dependendo da ocorrencia de certos eventos.
  • Qualquer alteração de dados não ocorre de modo atomic, ou seja no caso de se haver perda de energia ou falha durante a atualização, o campo não será restaurado para valores padrões.
  • Um objeto transiente pode referenciar um campo em um objeto persistente.
  • Um campo em um objeto transiente pode referenciar um objeto persistente.
  • Se um objeto transiente não for refenciado por outro objetos, ele se torna permanentemente perdido e a área de memória só poderá ser recuperada através do Garbage Collector.
  • A escrita em objetos transientes costuma ser 1000x mais rápida pois os dados são escritos na Memória RAM.
Objetos transientes devem ser usados como pequenas áreas temporárias pelo Applet, em que dados são frequentemente modificados e não necessitam ser persistentes.
Utilizando objetos transiente também reduz a escrita na EEPROM aumentando o tempo de vida útil da mesma, a performance e a segurança sobre dados sensíveis como chaves criptográficas de sessão.
Em contra partida use com moderação pois a memória RAM é pequena em capacidade cerca de 20x menos que a EEPROM.
Existem dois tipos de Objetos Transientes CLEAR_ON_RESET e CLEAR_ON_DESELECT, que basicamente associa o evento em que o JCRE irá limpar os campos do objeto. CLEAR_ON_RESET, esse tipo de Objeto mantém seus dados íntegros durante a seleção de outros Applets, é usado geralmente nos casos em que Applets compartilham ou trabalham em conjunto ou mesmo para aplicações que trabalhem com diversos Applets em uma mesma sessão, ou seja os dados são perdidos em caso de reset ou perda de Energia. CLEAR_ON_DESELECT, esse tipo de Objeto mantém seus dados integros enquanto um Applet permanecer Selecionado, ou seja os dados são perdidos se outro Applet for selecionado, em caso de reset ou perda de Energia.
Criando Objetos Transientes Conforme dito acima Objetos Transientes são criados através de uma chamada à API Java Card JCSystem.
  1. /** 
  2.  * Cria um Array Transiente boolean 
  3.  * @param length - Tamanho do Array Transiente 
  4.  * @param event - CLEAR_ON_RESET ou CLEAR_ON_DESELECT 
  5.  * @return - Array 
  6.  */  
  7. public static boolean[]  
  8.     makeTransientBooleanArray(short length, byte event)  
  9.   
  10. /** 
  11.  * Cria um Array Transiente byte 
  12.  * @param length - Tamanho do Array Transiente 
  13.  * @param event - CLEAR_ON_RESET ou CLEAR_ON_DESELECT 
  14.  * @return - Array 
  15.  */  
  16. public static byte[]  
  17.     makeTransientByteArray(short length, byte event)  
  18.   
  19. /** 
  20.  * Cria um Array Transiente short 
  21.  * @param length - Tamanho do Array Transiente 
  22.  * @param event - CLEAR_ON_RESET ou CLEAR_ON_DESELECT 
  23.  * @return - Array 
  24.  */  
  25. public static short[]  
  26.     makeTransientShortArray(short length, byte event)  
  27.   
  28. /** 
  29.  * Cria um Array Transiente Object 
  30.  * @param length - Tamanho do Array Transiente 
  31.  * @param event - CLEAR_ON_RESET ou CLEAR_ON_DESELECT 
  32.  * @return - Array 
  33.  */  
  34. public static Object[]  
  35.     makeTransientObjectArray(short length, byte event)  
O Seguinte Fragmento de Código demonstra como é criado um objeto transiente:
  1. //Cria um Buffer de 10 Elementos Limpos on reset  
  2. byte[] buffer =  
  3.     JCSystem.makeTransientByteArray(10,JCSystem.CLEAR_ON_RESET);  

Verificando por objetos transientes  Quando um Applet necessita acessar objetos criados por outros Applets, a classe JCSystem oferece um método para verificar o tipo de objeto através do seguinte método:
  1. public static byte isTransient(Object theObject)  
O Método isTransient retorna um dos tipos (CLEAR_ON_RESET ou CLEAR_ON_DESELECT) ou uma constante JCSystem.NOT_A_TRANSIENT_OBJECT para indicar se um Objeto é null ou persistente.
Memory Leak um risco real Sempre que um Applet é Apagado ou De-Instanciado, todos os recursos alocados serão liberados automaticamente pelo JCRE, porém existe riscos de vazamento de memória, e no Java Card caso não tenha um Garbage Collector implementado a memória é perdida permanentemente. Considere o seguinte exemplo:
  1. public class MyApplet extends Applet {  
  2.   
  3.   //Buffer de teste  
  4.   private byte[] buffer;  
  5.    
  6.   //Construtor  
  7.   public MyApplet(){  
  8.   
  9.    //Cria um Objeto Persistente de 10 Bytes  
  10.    buffer = new byte[10];  
  11.   
  12.    register();  
  13.   }  
  14.   
  15.   //Um Metodo que causa Memory Leak  
  16.   public void MyMethod() {  
  17.   
  18.    //Realoca buffer com mais 20 bytes.  
  19.    buffer = new byte[20];  
  20.   
  21.    ...  
  22.   }  
  23. }  
Na linha 10 criamos um array de 10 bytes persistente no construtor. Na linha 19 um método aloca 20 bytes, porém nesse momento os 10 bytes alocados no construtor serão permanentemente pedidos caso não exista Garbage Collector implementado. E vale lembrar que nem todos os cartões possuem o GC implementado, logo muitíssimo cuidado ao trabalhar com a Memória de um Java Card. No exemplo acima, o correto seria na linha 10 criarmos um array de 20 bytes e eliminar a linha 19, com isso no momento de exclusão ou de-instanciamento do Applet os 20 bytes serão liberados automaticamente pelo JCRE. 
Colocando para funcionar Ok, após toda essa extensa explicação chegou a hora de testar-mos tudo o que vimos até agora.
  1. import javacard.framework.*;  
  2.   
  3. public class MemoryTest extends Applet {  
  4.  /** Instrução Read Memory */  
  5.  private static final byte INS_READ_MEMORY = 0x02;  
  6.  /** Instrução de Estrita na EEPROM */  
  7.  private static final byte INS_WRITE_EEPROM = 0x04;  
  8.  /** Instrução de Estrita na RAM */  
  9.  private static final byte INS_WRITE_RAM = 0x06;  
  10.  /** Tamanho do Objeto */  
  11.  private static final short ARRAY_SIZE = 10;  
  12.  /** Objeto Persistente */  
  13.  byte[] eeprom_ba;  
  14.  /** Objeto Transiente */  
  15.  byte[] ram_ba;  
  16.    
  17.  /** 
  18.   * Construtor 
  19.   */  
  20.  private MemoryTest() {  
  21.     
  22.   //Persistent, Objeto e Dados gravados na EEPROM  
  23.   this.eeprom_ba = new byte[ARRAY_SIZE];  
  24.     
  25.   //Transient, Objeto na EEPROM e Dados na RAM  
  26.   this.ram_ba = JCSystem.makeTransientByteArray(  
  27.                  ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT);  
  28.  }  
  29.   
  30.  /** 
  31.   * Installer 
  32.   * @param bArray 
  33.   * @param bOffset 
  34.   * @param bLength 
  35.   * @throws ISOException 
  36.   */  
  37.  public static void install(  
  38.   byte bArray[], short bOffset, byte bLength)  
  39.    throws ISOException   
  40.  {  
  41.   new MemoryTest().register();  
  42.  }  
  43.   
  44.  /** 
  45.   * Processa APDU's 
  46.   */  
  47.  public void process(APDU apdu){  
  48.     
  49.   //Adquire Referencia do buffer de entrada e saida  
  50.   byte buffer[] = apdu.getBuffer();  
  51.     
  52.   //Boa Prática: Retorna 9000 no SELECT  
  53.   if (selectingApplet())  
  54.    ISOException.throwIt(ISO7816.SW_NO_ERROR);  
  55.     
  56.   //Verifica Instrução enviada pelo Host  
  57.   switch ( buffer[ISO7816.OFFSET_INS] ){  
  58.     
  59.   //Instrução de leitura de Memória  
  60.   case INS_READ_MEMORY :   
  61.   {  
  62.    short i;  
  63.      
  64.    //Copia os 10 bytes do Objeto Persistente  
  65.    //seguido dos 10 bytes do Objeto Transiente  
  66.    for ( i = 0 ; i < ARRAY_SIZE ; i++ ){  
  67.     buffer[i] = eeprom_ba[i];  
  68.     buffer[i + ARRAY_SIZE] = ram_ba[i];  
  69.    }  
  70.      
  71.    //Sinaliza JCRE que retornará dados  
  72.    apdu.setOutgoingAndSend(  
  73.     (short0, (short)(ARRAY_SIZE * (short2));  
  74.   
  75.    //Rertorna dados e 0x9000  
  76.    ISOException.throwIt(ISO7816.SW_NO_ERROR);  
  77.   }  
  78.     
  79.   //Instrução de Escrita na EEPROM  
  80.   case INS_WRITE_EEPROM :  
  81.   {  
  82.    //Adquire Quantidade de dados Recebidos  
  83.    short count = apdu.setIncomingAndReceive();  
  84.      
  85.    //Se Quantidade de Dados Recebidos Incorreto  
  86.    if ( count != ARRAY_SIZE )  
  87.     ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);  
  88.      
  89.    //Copia dados Recebidos para o objeto Persistente  
  90.    Util.arrayCopy(  
  91.     buffer, ISO7816.OFFSET_CDATA, eeprom_ba, (short0, count);  
  92.   
  93.    //Rertorna 0x9000  
  94.    ISOException.throwIt(ISO7816.SW_NO_ERROR);  
  95.   }  
  96.   
  97.   //Instrução de Escrita na RAM  
  98.   case INS_WRITE_RAM :  
  99.   {  
  100.    //Adquire Quantidade de dados Recebidos  
  101.    short count = apdu.setIncomingAndReceive();  
  102.      
  103.    //Se Quantidade de Dados Recebidos Incorreto  
  104.    if ( count != ARRAY_SIZE )  
  105.     ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);  
  106.      
  107.    //Copia dados Recebidos para o objeto Transiente  
  108.    Util.arrayCopy(  
  109.     buffer, ISO7816.OFFSET_CDATA, ram_ba, (short0, count);  
  110.   
  111.    //Rertorna 0x9000  
  112.    ISOException.throwIt(ISO7816.SW_NO_ERROR);  
  113.   }  
  114.     
  115.   }//Switch END  
  116.   
  117.   //Boa practica: Se não conhece a Instrução apenas diga:  
  118.   ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);  
  119.  }  
  120. }  
Infelizmente para testar com eficiência esse código é necessário um cartão Java Card real ou simula-lo no CREF(C Reference Implementation Simulator), porém o CREF é de difícil configuração e seu Applet deve ser instalado da mesma forma como se instala em um cartão físico, e a memória EEPROM deve ser gravada em um arquivo no HD para se simular a persistencia, porém lidar com todas essas variáveis do CREF está fora do Escopo desse Artigo. Na linha 23 criamos um Objeto Persistente que se trata de um array de 10 bytes que será armazenado na EEPROM tanto a referência como o campo de dados. Na linha 26 criamos um Objeto Transiente utilizando a API Java Card que se trata de um array de 10 bytes que será armazenado na EEPROM apenas a referencia desse objeto, enquanto o campo de dados será aramazenada na RAM. Da linha 60 a 77 fica a porção de código responsável pela leitura dos dados dos Objetos. Da linha 80 a 95 fica a porção de código que irá gravar os dados contidos na área de Dados do APDU no objeto persistente. E finalmente da linha 98 a 113 fica a porção de código que irá gravar os dados contidos na área de Dados do APDU no objeto transiente.
Para ler a memória basta enviar o seguinte APDU:
0x00 0x02 0x00 0x00 0x00

Para Escrever 10  FF's na EEPROM :
0x00 0x04 0x00 0x00 0x0A 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF

Para Escrever 10  AA's na RAM :
0x00 0x06 0x00 0x00 0x0A 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 
Com isso vocês poderão testar a natureza de Objetos Persistent e Transient do Java Card.Existe ainda 2 tópicos relacionados a Memória a Atomicidade e a Transação, o qual abordarei ainda em artigos futuros aguardem.


Fonte: http://planetsmartcards.blogspot.com.br