Bruno Emerson Gurgel Gomes Desenvolvimento Formal de Aplicações para Smart Cards Natal-RN 2012 U NIVERSIDADE F EDERAL DO R IO G RANDE DO N ORTE C ENTRO DE C IÊNCIAS E XATAS E DA T ERRA P ROGRAMA DE P ÓS - GRADUAÇÃO EM S ISTEMAS E C OMPUTAÇÃO Bruno Emerson Gurgel Gomes Desenvolvimento Formal de Aplicações para Smart Cards Tese de doutorado em ciência da computação. Orientador: Prof. Dr. David Boris Paul Déharbe Natal-RN 2012 Agradecimentos A pesquisa que deu origem a este trabalho teve início no ano de 2005, quando tive a oportunidade de ingressar como bolsista de iniciação científica, sob orientação de Anamaria Moreira e colaboração de David Déharbe. A eles o meu especial agradecimento, pelo conhecimento adquirido e pela amizade durante todos esses anos. Com eles aprendi bastante e amadureci como pesquisador e como pessoa. Gostaria de agradecer a toda a minha família, Wellington e Elizabeth (pais), Edmundo e Vládina (sogro e sogra), que me forneceram suporte material e emocional para seguir adiante. O carinho e incentivo dedicados foram fundamentais para mim. À Vanessa, minha esposa, que esteve ao meu lado desde o início, pelo companheirismo, respeito e amor. Você pacientemente suportou meu mal-humor e, dia após dia, me deu forças para seguir adiante, mesmo em face das dificuldades e angústias no decorrer da construção da tese. Apesar de não compreender a parte técnica do trabalho, as suas revisões criteriosas no texto foram de fundamental importância. Agradeço também a todos os que colaboraram com o trabalho, dentre os quais destaco Kátia Moraes, Thiago Dutra, Giuliano Vilela e Simone Santos. O auxílio e a troca de conhecimentos com vocês foi inestimável. Aos professores membros externos da banca, Augusto Sampaio, Marcel Oliveira e Rohit Gheyi, pelas observações que ajudaram a melhorar o resultado final deste trabalho, e que servirão de base para o seu aprimoramento daqui em diante. Aos colegas (amigos) da pós-graduação, Cleverton, Plácido, Isaac, Macilon, Fred, dentre tantos outros, pelos momentos de descontração, de compartilhamento de ideias e de ajuda em diversos momentos. Em especial agradeço a Cleverton Hentz, pelo apoio no desenvolvimento inicial da especificação da tradução em ASF+SDF. Por fim, agradeço aos amigos e alunos do IFRN - câmpus Currais Novos. Nesses dois anos e meio na instituição, conquistei a amizade e o respeito de todos e pude crescer como ser humano e professor. Um abraço especial a Miguel Kolodiuk, Carlos “Stiff” Gustavo e Álvaro Hermano, amigos com os quais sei que posso contar por toda a vida. Resumo As aplicações para smart cards representam um mercado que cresce a cada ano. Normalmente, essas aplicações manipulam e armazenam informações que requerem garantias de segurança, tais como valores monetários ou informações confidenciais. A qualidade e a segurança do software para cartões inteligentes pode ser aprimorada através de um processo de desenvolvimento rigoroso que empregue técnicas formais da engenharia de software. Neste trabalho propomos o método BSmart, uma especialização do método formal B dedicada ao desenvolvimento de aplicações para smart cards na linguagem Java Card. O método descreve, em um conjunto de etapas, como uma aplicação smart card pode ser gerada a partir de refinamentos em sua especificação formal. O desenvolvimento é suportado por um conjunto de ferramentas, automatizando a geração de parte dos refinamentos e a tradução para as aplicações Java Card cliente (host) e servidora (applet). Ressalta-se que o processo de especificação e refinamento descrito no método foi formalizado e verificado utilizando o próprio método B, com o auxílio da ferramenta Atelier B [Cle12a]. Destaca-se que a aplicação Java Card é traduzida a partir do último passo de refinamento, denominado de implementação. A especificação dessa tradução foi feita na linguagem ASF+SDF [BKV08]. Inicialmente, descreveu-se as gramáticas das linguagens B e Java (SDF) e, em uma etapa posterior, especificou-se as transformações de B para Java Card através de regras de reescrita de termos (ASF). Essa abordagem foi um importante auxílio durante o processo de tradução, além de servir ao propósito de documentálo. Cumpre destacar a biblioteca KitSmart [Dut06, San12], componente essencial ao método BSmart, que inclui modelos em B de todas as 93 classes/interfaces da API Java Card na versão 2.2.2, dos tipos de dados Java e Java Card e de máquinas que podem ser úteis ao especificador, mas que não estão presentes na API padrão. Tendo em vista validar o método, seu conjunto de ferramentas e a biblioteca KitSmart, procedeu-se com o desenvolvimento, seguindo o método BSmart, de uma aplicação de passaporte eletrônico. Os resultados alcançados neste trabalho contribuem para o desenvolvimento smart card, na medida em que possibilitam a geração de aplicações Java Card completas (cliente e servidor) e menos sujeitas a falhas. Palavras-chave: Smart Cards, Java Card, Método Formal B, Refinamento, Desenvolvimento Formal, Geração de Código. Abstract Smart card applications represent a growing market. Usually this kind of application manipulate and store critical information that requires some level of security, such as financial or confidential information. The quality and trustworthiness of smart card software can be improved through a rigorous development process that embraces formal techniques of software engineering. In this work we propose the BSmart method, a specialization of the B formal method dedicated to the development of smart card Java Card applications. The method describes how a Java Card application can be generated from a B refinement process of its formal abstract specification. The development is supported by a set of tools, which automates the generation of some required refinements and the translation to Java Card client (host) and server (applet) applications. With respect to verification, the method development process was formalized and verified in the B method, using the Atelier B tool [Cle12a]. We emphasize that the Java Card application is translated from the last stage of refinement, named implementation. This translation process was specified in ASF+SDF [BKV08], describing the grammar of both languages (SDF) and the code transformations through rewrite rules (ASF). This specification was an important support during the translator development and contributes to the tool documentation. We also emphasize the KitSmart library [Dut06, San12], an essential component of BSmart, containing models of all 93 classes/interfaces of Java Card API 2.2.2, of Java/Java Card data types and machines that can be useful for the specifier, but are not part of the standard Java Card library. In other to validate the method, its tool support and the KitSmart, we developed an electronic passport application following the BSmart method. We believe that the results reached in this work contribute to Java Card development, allowing the generation of complete (client and server components), and less subject to errors, Java Card applications. Keywords: Smart Cards, Java Card, B Formal Method, Refinement, Formal Development, Code Generation. Sumário Lista de Figuras 1 2 Introdução p. 16 1.1 Justificativa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 18 1.2 Desenvolvimento com o Método BSmart . . . . . . . . . . . . . . . . . . . . p. 20 1.3 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 21 1.3.1 Objetivo geral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 21 1.3.2 Objetivos específicos . . . . . . . . . . . . . . . . . . . . . . . . . . p. 21 1.4 Artigos Publicados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 23 1.5 Resumo dos Capítulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 24 Java Card p. 26 2.1 O Modelo de Comunicação Smart Card . . . . . . . . . . . . . . . . . . . . p. 27 2.2 Desenvolvimento de Aplicações Java Card . . . . . . . . . . . . . . . . . . . p. 28 2.3 O applet Java Card . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 30 2.3.1 Applet APDU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 30 2.3.2 Applet RMI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 33 Aplicação Host . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 35 2.4 3 O Método Formal B p. 38 3.1 Especificação Abstrata (Máquina B) . . . . . . . . . . . . . . . . . . . . . . p. 40 3.1.1 Substituições e obrigações de prova . . . . . . . . . . . . . . . . . . p. 43 3.1.2 Substituições generalizadas . . . . . . . . . . . . . . . . . . . . . . p. 45 3.2 3.3 4 p. 48 3.2.1 Obrigações de prova . . . . . . . . . . . . . . . . . . . . . . . . . . p. 49 3.2.2 Implementação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 50 O Formalismo Event-B . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 51 O Método BSmart p. 56 4.1 Processo de Desenvolvimento . . . . . . . . . . . . . . . . . . . . . . . . . p. 57 4.2 Especificação Abstrata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 59 4.3 Desenvolvimento da Aplicação Java Card . . . . . . . . . . . . . . . . . . . p. 61 4.3.1 Geração do refinamento Full Function . . . . . . . . . . . . . . . . . p. 63 4.3.2 Implementação em B do desenvolvimento Java Card dos Serviços . . p. 65 4.3.3 Implementação B do applet Java Card . . . . . . . . . . . . . . . . . p. 66 Verificação da Mudança de Interface entre os Modelos Java e Java Card . . . p. 67 4.4.1 Um método de “refinamento” para permitir a mudança de interface . p. 68 Geração da API para a Aplicação Host . . . . . . . . . . . . . . . . . . . . . p. 70 4.5.1 Exemplo de classes geradas . . . . . . . . . . . . . . . . . . . . . . p. 73 Suporte de Ferramentas ao Método BSmart . . . . . . . . . . . . . . . . . . p. 75 4.6.1 Ferramentas e APIs Desenvolvidas . . . . . . . . . . . . . . . . . . . p. 76 4.7 Uso de Retrenchment para a verificação da mudança de interface . . . . . . . p. 78 4.8 Avaliação do Método e Perspectivas Futuras . . . . . . . . . . . . . . . . . . p. 83 4.4 4.5 4.6 5 Refinamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Especificação da Tradução para Java Card p. 85 5.1 Definição Sintática de Linguagens com SDF . . . . . . . . . . . . . . . . . . p. 86 5.1.1 Símbolos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 88 5.1.2 Seções da gramática . . . . . . . . . . . . . . . . . . . . . . . . . . p. 89 ASF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 91 5.2.1 p. 92 5.2 Reescrita de termos na linguagem ASF . . . . . . . . . . . . . . . . 5.2.2 5.3 5.4 6 p. 93 Traduzindo de B para Java Card . . . . . . . . . . . . . . . . . . . . . . . . p. 94 5.3.1 Parte SDF da tradução . . . . . . . . . . . . . . . . . . . . . . . . . p. 95 5.3.2 Regras de reescrita globais . . . . . . . . . . . . . . . . . . . . . . . p. 96 5.3.3 Predicados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 98 5.3.4 Termos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 99 5.3.5 Cláusulas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 100 5.3.6 Condições . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 105 5.3.7 Instruções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 106 Considerações Finais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 109 KitSmart: uma biblioteca de auxílio ao desenvolvimento Java Card em B 6.1 6.2 7 Componentes de uma especificação ASF . . . . . . . . . . . . . . . p. 111 Módulos Básicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 112 6.1.1 Tipos primitivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 112 6.1.2 Outros Módulos de Biblioteca . . . . . . . . . . . . . . . . . . . . . p. 114 Especificação das classes da API Java Card . . . . . . . . . . . . . . . . . . p. 114 6.2.1 Características gerais dos modelos . . . . . . . . . . . . . . . . . . . p. 116 6.2.2 Exemplo: modelo da classe APDU . . . . . . . . . . . . . . . . . . . p. 117 6.2.3 Abordagem para exceções . . . . . . . . . . . . . . . . . . . . . . . p. 119 6.2.4 Atribuição de tipos a objetos . . . . . . . . . . . . . . . . . . . . . . p. 120 6.2.5 Sobrecarga e sobrescrita de métodos . . . . . . . . . . . . . . . . . . p. 121 6.3 Módulos para tarefas úteis ao desenvolvedor . . . . . . . . . . . . . . . . . . p. 122 6.4 Considerações Finais e Desenvolvimentos Futuros . . . . . . . . . . . . . . . p. 123 Estudo de Caso: Passaporte Eletrônico p. 125 7.1 Principais referências utilizadas . . . . . . . . . . . . . . . . . . . . . . . . p. 127 7.2 Passaporte Eletrônico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 128 7.3 8 7.2.1 Página de dados do passaporte . . . . . . . . . . . . . . . . . . . . . p. 129 7.2.2 Estrutura para armazenamento de dados (LDS) . . . . . . . . . . . . p. 129 7.2.3 Mecanismos de Segurança das Informações Armazenadas . . . . . . p. 131 7.2.4 Protocolos de Segurança . . . . . . . . . . . . . . . . . . . . . . . . p. 134 Especificação inicial dos serviços do Passaporte . . . . . . . . . . . . . . . . p. 137 7.3.1 Serviço de geração de número randômico . . . . . . . . . . . . . . . p. 138 7.3.2 Serviço de autenticação mútua (cliente-cartão) . . . . . . . . . . . . p. 138 7.3.3 Serviço de leitura de arquivos . . . . . . . . . . . . . . . . . . . . . p. 140 7.4 Especificação Formal Abstrata . . . . . . . . . . . . . . . . . . . . . . . . . p. 140 7.5 Versão Java Card da Máquina Passport . . . . . . . . . . . . . . . . . . . . . p. 144 7.6 Refinamento Full Function . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 145 7.7 Implementação B do Passaporte . . . . . . . . . . . . . . . . . . . . . . . . p. 147 7.7.1 Máquinas externas e estado da implementação . . . . . . . . . . . . p. 147 7.7.2 Operações processGetChallenge e processSelectFile . . . . . . . . . p. 149 7.7.3 Desenvolvimento do Applet Java Card . . . . . . . . . . . . . . . . . p. 151 7.8 Verificação do Refinamento entre Especificação Abstrata e o Modelo Java Cardp. 155 7.9 Considerações Finais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 157 Trabalhos Relacionados 8.1 p. 159 Ferramentas de Suporte ao Desenvolvimento Formal de Software em B e Event-B . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 160 9 8.2 Verificação de Aplicações Smart Cards . . . . . . . . . . . . . . . . . . . . . p. 163 8.3 Geração de Código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 167 Considerações Finais 9.1 p. 172 Principais Contribuições da Tese . . . . . . . . . . . . . . . . . . . . . . . . p. 173 9.1.1 Correção do Método BSmart . . . . . . . . . . . . . . . . . . . . . . p. 174 9.1.2 Suporte de Ferramentas ao Método . . . . . . . . . . . . . . . . . . p. 175 9.1.3 Estudo de Caso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 175 9.2 O Método Formal B aplicado ao Desenvolvimento Smart Card . . . . . . . . p. 176 9.3 Trabalhos Futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 178 9.3.1 Método BSmart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 178 9.3.2 Ferramentas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 178 9.3.3 Otimização de Código . . . . . . . . . . . . . . . . . . . . . . . . . p. 179 9.3.4 KitSmart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 180 9.3.5 Estudo de Caso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 181 Referências p. 182 Apêndice A -- Alguns componentes do KitSmart p. 190 A.1 Tipos básicos - JShort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 190 A.2 Tipos Básicos - JBoolean . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 191 A.3 API Java Card - APDU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 192 Apêndice B -- Especificação da Tradução para Java Card em ASF+SDF p. 196 B.1 B02Java.sdf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 196 B.2 B02Java.asf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 198 B.2.1 Regras Globais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 198 B.2.2 Cláusulas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 198 B.2.3 Instruções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 201 B.2.4 Termos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 205 B.2.5 Condições . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 206 B.2.6 Predicados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 207 Apêndice C -- Especificação do Passaporte Eletrônico p. 210 C.1 Máquina Abstrata Passport . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 210 C.2 Máquina Abstrata Java Card - Passport_JC . . . . . . . . . . . . . . . . . . . p. 212 C.3 Refinamento Full Function - Passport_JC_FF_ref . . . . . . . . . . . . . . . p. 214 C.4 Implementação Principal dos Serviços - Passport_JC_imp . . . . . . . . . . . p. 216 C.4.1 Passport_JC_imp - Operação processReadBinary . . . . . . . . . . . p. 217 C.4.2 Passport_JC_imp - Operação processGetChallenge . . . . . . . . . . p. 218 C.4.3 Passport_JC_imp - Operação processMutualAuthenticate . . . . . . . p. 219 C.5 Máquina Abstrata Applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 220 C.6 Refinamento Applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 222 C.7 Implementação Applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 223 C.8 Refinamento para a verificação da conformidade entre o modelo abstrato (Passport) e o concreto (Passport_JC) . . . . . . . . . . . . . . . . . . . . . p. 225 Lista de Figuras 2.1 Componentes da plataforma Java Card. Adaptado de: [Ort03a] . . . . . . . . p. 27 2.2 Comando e resposta APDU. Fonte: [Ort03a] . . . . . . . . . . . . . . . . . . p. 28 2.3 Um applet Java Card APDU para bilhetagem eletrônica - definição de atributos e métodos herdados da classe Applet. . . . . . . . . . . . . . . . . . . p. 31 2.4 Método de serviço addCredit do applet Transport. . . . . . . . . . . . . . . . p. 32 2.5 Método de serviço getCredits do applet Transport. . . . . . . . . . . . . . . p. 33 2.6 Interface remota do Applet RMI . . . . . . . . . . . . . . . . . . . . . . . . p. 34 2.7 Implementação dos serviços do applet RMI . . . . . . . . . . . . . . . . . . p. 34 2.8 Implementação da classe applet na versão RMI . . . . . . . . . . . . . . . . p. 35 2.9 Um exemplo simples de aplicação host para o applet Transport. . . . . . . . p. 36 3.1 Um modelo de uma máquina B simples. . . . . . . . . . . . . . . . . . . . . p. 41 3.2 Esquema genérico de um refinamento. . . . . . . . . . . . . . . . . . . . . . p. 48 3.3 Um protótipo do módulo de implementação. . . . . . . . . . . . . . . . . . . p. 51 3.4 Máquina Event-B Transport (esq.) e contexto Transport_Constants (dir.) . . . p. 52 3.5 Eventos da especificação Transport . . . . . . . . . . . . . . . . . . . . . . . p. 53 3.6 Ações presentes em Event-B. . . . . . . . . . . . . . . . . . . . . . . . . . . p. 54 4.1 Desenvolvimento com o método BSmart. . . . . . . . . . . . . . . . . . . . p. 57 4.2 Definição do tipo Java e das propriedades para a formalização dos componentes da especificação abstrata. . . . . . . . . . . . . . . . . . . . . . . . . p. 60 4.3 Representação da especificação abstrata que inicia o desenvolvimento BSmart. p. 60 4.4 Uma visão dos artefatos que compõem o desenvolvimento da aplicação Java Card do lado cartão (não inclui aplicação cliente). . . . . . . . . . . . . . . . p. 61 4.5 Versão “Java Card” da máquina J_App. . . . . . . . . . . . . . . . . . . . . 4.6 Definição do tipo Java Card e das propriedades para a formalização dos com- p. 62 ponentes da especificação concreta. . . . . . . . . . . . . . . . . . . . . . . p. 63 4.7 Versão full function de JC_App (esq.) e máquina de contexto (dir.). . . . . . p. 64 4.8 Implementação B da aplicação Java Card. . . . . . . . . . . . . . . . . . . . p. 65 4.9 Refinamento da máquina Applet da API Java Card. . . . . . . . . . . . . . . p. 66 4.10 Implementação B do applet Java Card. . . . . . . . . . . . . . . . . . . . . . p. 67 4.11 Refinamento da especificação abstrata inicial para verificar a correção da mudança de interface pelo módulo Java Card. . . . . . . . . . . . . . . . . . . . p. 69 4.12 A máquina InterfaceContext. . . . . . . . . . . . . . . . . . . . . . . . . . . p. 70 4.13 Diagrama de classes UML descrevendo os componentes da API para a aplicação cliente. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 72 4.14 Especificação abstrata Transport. . . . . . . . . . . . . . . . . . . . . . . . . p. 73 4.15 Classe TransportCommunication. . . . . . . . . . . . . . . . . . . . . . . . . p. 74 4.16 Classe TransportProxy - operação addCredit. . . . . . . . . . . . . . . . . . p. 74 4.17 Classe TransportProxy - operações debit e getBalance. . . . . . . . . . . . . p. 75 4.18 Ferramentas de suporte ao método BSmart. . . . . . . . . . . . . . . . . . . p. 77 4.19 Máquina B padrão (esquerda) e máquina retrenchment (direita). . . . . . . . p. 80 4.20 Retrenchment Java Card da especificação abstrata principal J_App (Fig. 4.2). p. 82 5.1 Componentes das gramáticas B0 e Java . . . . . . . . . . . . . . . . . . . . p. 87 5.2 Elementos básicos de uma definição SDF . . . . . . . . . . . . . . . . . . . p. 88 5.3 Módulo Lexical da gramática B0 em SDF. . . . . . . . . . . . . . . . . . . . p. 90 5.4 Módulo Instruction da gramática B0 em SDF. . . . . . . . . . . . . . . . . . p. 92 5.5 Símbolos iniciais das gramáticas B e Java. . . . . . . . . . . . . . . . . . . . p. 95 5.6 Parte SDF especificação da tradução. . . . . . . . . . . . . . . . . . . . . . . p. 96 5.7 Regras iniciais do processo de tradução . . . . . . . . . . . . . . . . . . . . p. 97 5.8 Regras de reescrita para predicados de tipagem . . . . . . . . . . . . . . . . p. 98 5.9 Regras para rescrita de termos básicos . . . . . . . . . . . . . . . . . . . . . p. 99 5.10 Regras para rescrita de termos funcionais . . . . . . . . . . . . . . . . . . . p. 100 5.11 Regras para rescrita de expressões aritméticas. . . . . . . . . . . . . . . . . . p. 100 5.12 Regras de reescrita para uma sequência de zero ou mais cláusulas . . . . . . . p. 101 5.13 Equações de reescrita para cláusulas de inclusão de máquinas. . . . . . . . . p. 101 5.14 Regras de reescrita para uma lista de conjuntos . . . . . . . . . . . . . . . . p. 102 5.15 Regras para reescrita de conjuntos adiados e enumerados. . . . . . . . . . . . p. 103 5.16 Regras para tradução de constantes. . . . . . . . . . . . . . . . . . . . . . . p. 103 5.17 Tradução do invariante e da inicialização . . . . . . . . . . . . . . . . . . . . p. 104 5.18 Parte das regras de rescrita para tradução de operações. . . . . . . . . . . . . p. 104 5.19 Regra auxiliar para tradução dos parâmetros das operações . . . . . . . . . . p. 105 5.20 Regra para a tradução de condições. . . . . . . . . . . . . . . . . . . . . . . p. 105 5.21 Equação de reescrita para uma lista de instruções. . . . . . . . . . . . . . . . p. 106 5.22 Reescrita das instruções skip, begin-end e becomes equal to. . . . . . . . . . p. 106 5.23 Reescrita das instruções chamada de operação e definição de variável local. . p. 107 5.24 Equação de reescrita para condicional (versão com else). . . . . . . . . . . . p. 107 5.25 Equação de reescrita para condicional (versão com elsif). . . . . . . . . . . . p. 108 5.26 Reescrita da instrução case. . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 108 5.27 Equação para tradução da instrução while. . . . . . . . . . . . . . . . . . . . p. 109 6.1 Modelo em B para o tipo short. . . . . . . . . . . . . . . . . . . . . . . . . . p. 113 6.2 Parte da máquina JBoolean. . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 114 6.3 Definição do estado e algumas operações da biblioteca para sequência de byte. p. 115 6.4 Quantidade de classes por pacote na API 2.2.2. Fonte [San12]. . . . . . . . . p. 116 6.5 Máquina Apdu_Properties. . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 117 6.6 Definição do estado da máquina Apdu. . . . . . . . . . . . . . . . . . . . . . p. 118 6.7 Operações da máquina Apdu relacionadas ao envio de informações ao cliente. p. 119 6.8 Condições de exceção e seus comentários associados na pré-condição da operação setOutgoingLength. . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 120 6.9 Definição do estado da máquina APDU com uma estrutura representando o tipo APDU. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 121 6.10 Duas versões do método register da máquina Applet. . . . . . . . . . . . . . p. 122 6.11 Sobrescrita do método process como um refinamento de Applet. . . . . . . . p. 122 6.12 Parte da máquina Date. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 123 7.1 Exemplo de página de dados de um passaporte. Fonte [ICA06] . . . . . . . . p. 129 7.2 Máquina Passport - máquinas referenciadas e variáveis de estado. . . . . . . . p. 141 7.3 Máquina Passport - operação processGetChallenge. . . . . . . . . . . . . . . p. 142 7.4 Máquina Passport - operação processMutualAuthenticate. . . . . . . . . . . . p. 143 7.5 Máquina Passport - operação selectFile. . . . . . . . . . . . . . . . . . . . . p. 143 7.6 Máquina Passport - operação processReadBinary. . . . . . . . . . . . . . . . p. 144 7.7 Interface das operações da máquina Passport_JC. . . . . . . . . . . . . . . . p. 144 7.8 Máquinas referenciadas pelo refinamento Full Function. . . . . . . . . . . . . p. 145 7.9 Máquina de contexto Passport_JC_FF_Context. . . . . . . . . . . . . . . . . p. 146 7.10 Versão full function (direita) da operação processSelectFile. . . . . . . . . . . p. 146 7.11 Visão geral dos módulos principais do desenvolvimento Passport. . . . . . . . p. 147 7.12 Máquinas referenciadas e especificação do estado da implementação Passport_JC_imp. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 148 7.13 Passport_JC_imp - operação concreta processGetChallenge. . . . . . . . . . p. 149 7.14 Passport_JC_imp - operação concreta processSelectFile. . . . . . . . . . . . p. 150 7.15 Módulos referenciados e estado da máquina Applet. . . . . . . . . . . . . . . p. 151 7.16 Operações abstratas process e processAPDU (parte). . . . . . . . . . . . . . p. 152 7.17 Implementação do método process - obtenção de parâmetros e entrada de dados.p. 153 7.18 Implementação do método process - chamada de processAPDU . . . . . . . p. 154 7.19 Implementação do método process - saída de dados. . . . . . . . . . . . . . . p. 154 7.20 Implementação do método processAPDU . . . . . . . . . . . . . . . . . . . p. 155 7.21 Refinamento Passport_ref, máquinas inclusas e invariante. . . . . . . . . . . p. 156 7.22 Máquina Interface Context. . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 156 7.23 Operação refinada processGetChallenge. . . . . . . . . . . . . . . . . . . . . p. 157 16 1 Introdução Um smart card é um dispositivo computacional portátil, sob a forma de um chip de pequenas dimensões, normalmente embutido em um cartão plástico. O poder de processamento e armazenamento dos smart cards atuais é bastante limitado. Em sua maioria, eles possuem processadores com frequência de até 32MHz e memória não-volátil de até 128K. Apesar de limitados, esses recursos são suficientes para atender ao principal propósito da tecnologia, a saber, possibilitar a portabilidade de informações confidenciais em um ambiente com maiores garantias de segurança, quando comparado com os cartões de tarja magnética tradicionais. Os smart cards ainda se diferenciam pelo fornecimento de serviços através de pequenas aplicações inseridas no cartão. Nos últimos anos, os serviços fornecidos pelas aplicações para smart cards vêm se tornando cada vez mais presentes no cotidiano de boa parte da população mundial, em uma vasta gama de setores, tais como, finanças, transporte público, Internet e aplicações governamentais. De acordo com uma pesquisa conduzida pela Eurosmart [Eur12], uma associação de empresas da indústria smart card que tem por objetivo propor e regular padrões para essa tecnologia, no ano de 2011, cerca de 6, 3 bilhões de smart cards foram vendidos em todo o mundo. Em sua maioria, esses cartões foram adquiridos pelos setores de telecomunicações (cartões para a tecnologia GSM), finanças (cartões de crédito e pagamento) e governo (passaporte e assistência à saúde). Para o ano de 2012, a associação Eurosmart estima que o número de cartões vendidos possa ultrapassar a marca de 7 bilhões de unidades. Observa-se em relação ao Brasil a mesma tendência de crescimento do uso de cartões inteligentes em nível mundial. O bilhete eletrônico no transporte público de massa, a telefonia celular e o governo federal são os principais usuários da tecnologia. O setor governamental recentemente vem adotando duas importantes aplicações que utilizam smart cards para identificação pessoal e autenticação, a saber, o passaporte eletrônico e o registro de identidade civil (RIC). O passaporte eletrônico brasileiro vem sendo emitido desde o início do ano de 2011, se- 17 guindo os padrões e recomendações da International Civil Aviation Organization (ICAO). Ele consiste no documento tradicional de passaporte incluindo um chip smart card que armazena as informações pessoais do portador, dados biométricos (fotografia e impressões digitais) e um certificado digital que permite a autenticação do chip perante os terminais de leitura nos aeroportos ao redor do mundo, dentre outros mecanismos de segurança. No capítulo 7 detalha-se um estudo de caso desenvolvido neste trabalho com base em uma implementação de código aberto do passaporte eletrônico. Outra interessante aplicação smart card é o registro de identidade civil (RIC), uma solução segura que irá gradativamente substituir os documentos de identidade (RG). Cada brasileiro será identificado por um número nacional único, gerado a partir dos seus dados biométricos. Assim como o passaporte, o RIC contém elementos de identificação pessoal, biométrica e um certificado digital pessoal no chip smart card. Esse novo documento irá possibilitar uma maior segurança e menor burocracia na identificação pessoal, permitindo a assinatura de contratos e documentos por meio eletrônico [dJ12], até mesmo sem a presença física do interessado. Evidencia-se que uma das formas de se desenvolver aplicações para smart cards é utilizar a linguagem nativa do processador do cartão [RE03] ou uma linguagem de alto nível que compile para um hardware específico. Essa abordagem possui como principal vantagem o fato do programa desenvolvido ser capaz de ter acesso direto aos recursos do processador. Em contrapartida, a aplicação mencionada não é diretamente portável para outras arquiteturas e, normalmente, é mais difícil de codificar e manter do que aquelas desenvolvidas em linguagens de alto nível. Em outra abordagem de desenvolvimento, utilizada pela linguagem Java Card, a compilação é feita para um código intermediário que deve ser interpretado por uma máquina virtual. A linguagem Java Card possui ampla aceitação no mercado [RE03] e foi a escolha deste trabalho para o desenvolvimento smart card. Trata-se de uma versão da plataforma Java, com restrições quanto a recursos da linguagem, API fornecida e máquina virtual otimizada, tendo em vista possibilitar a sua utilização em dispositivos com recursos de hardware limitados, como os smart cards. Em comparação a linguagens nativas, Java Card gera aplicações que demandam um maior tempo de execução e consomem maiores recursos de processamento e memória. Entretanto, ela se beneficia das vantagens da linguagem Java padrão, como produtividade, portabilidade, linguagem segura quanto a tipos, desenvolvimento orientado a objetos e a ampla variedade de ferramentas disponíveis. O ambiente Java Card ainda fornece benefícios adicionais, dentre os quais ressalta-se a abstração de toda a camada de protocolos de baixo nível do sistema smart 18 card, a possibilidade de múltiplas aplicações serem instaladas em um mesmo cartão e mecanismos de segurança, como o controle de transação e o applet firewall, que possibilita que uma aplicação instalada tenha seus dados e código protegidos do acesso por outras aplicações. O restante desta introdução é divida nas seções descritas a seguir. A justificativa da tese é apresentada na seção 1.1. Posteriormente, na seção 1.2, introduz-se o método BSmart. Por sua vez, a seção 1.3 apresenta os objetivos da tese. Em seguida, a seção 1.4 descreve os trabalhos que foram publicados durante o desenvolvimento do trabalho. Por fim, a seção 1.5, apresenta um resumo dos capítulos que se seguem a esta introdução. 1.1 Justificativa A importância econômica desse mercado em contínua expansão torna evidente uma preocupação no domínio de aplicações para smart cards: a segurança das informações que encontramse armazenadas no cartão e que são enviadas ou recebidas no momento de uma transação. Essas informações, se interceptadas, alteradas, ou removidas, podem causar prejuízos graves ao portador do cartão, como perdas financeiras e a exposição de informações privilegiadas, como elementos de identificação pessoal, histórico de saúde, etc. A natureza portável dos smart cards os tornam ainda mais vulneráveis a ataques externos por software mal-intencionado. Essas aplicações podem ser instaladas em um cartão com o objetivo de explorar falhas no hardware ou em qualquer software presente no cartão. De forma complementar aos mecanismos de segurança oferecidos pelo hardware smart card ou por plataformas específicas, como Java Card, um meio de se prevenir comportamentos indesejáveis ou o acesso indevido a informações decorrente de erros introduzidos no desenvolvimento, é melhorar a qualidade do desenvolvimento das aplicações smart cards. A adoção de processos, métodos e ferramentas rigorosos da engenharia de software pode assegurar a entrega de um produto final com menos erros e em conformidade com os requisitos especificados. Nesse contexto, é possível inserir métodos formais no processo de desenvolvimento, que consistem em linguagens, técnicas e ferramentas, com forte embasamento matemático, utilizadas na especificação e verificação de sistemas [CW96]. Normalmente, eles não são aplicados ao desenvolvimento de todo um sistema, mas apenas em partes dele que definem funcionalidades críticas. Em regra, as técnicas formais não são empregadas de forma isolada, mas em conjunto com métodos tradicionais da engenharia de software para verificação e validação, como testes de software e inspeção de código. É importante observar que o emprego de um formalismo para a especificação e desenvol- 19 vimento de sistemas, tal como Z [Spi92], B [Abr96] ou Event-B [Abr10], não garante necessariamente que o software desenvolvido (ou parte dele) estará completamente livre de defeitos. Por outro lado, é um meio de assegurar a correção do seu comportamento de acordo com o que foi especificado [BH94]. Destaca-se que outro importante incentivo à utilização desses métodos formais reside no fato deles fornecerem um meio de documentação precisa adicional ao sistema. Nessa esteira, uma pesquisa conduzida por Lanet [Lan00] concluiu que as aplicações smart cards constituem-se em um domínio ideal para a adoção de métodos formais. Ele aponta como principais razões para isso o fato das aplicações e dos sistemas operacionais para smart cards serem de pequeno porte, a necessidade de segurança e a possibilidade de instalação de múltiplas aplicações em um mesmo cartão oferecida por tecnologias como Java Card ou MultOS [Mul10]. Conforme discutiu-se anteriormente, essa característica pode ser a porta de entrada para código mal-intencionado. Lanet argumenta que os métodos formais podem ser aplicados para reduzir erros no software smart card, certificar partes da aplicação em um alto nível de confiabilidade, submetendoas a entidades que utilizam-se de processos de certificação como o Common Criteria [Com10], e para reduzir a necessidade e o custo de testes. A pesquisa de Lanet também evidencia que o desenvolvimento de componentes reutilizáveis, de metodologias e ferramentas, bem como a integração de métodos formais com técnicas tradicionais, como a especificação em Unified Modelling Language (UML), é um meio de minimizar algumas restrições a uso de métodos formais, como o aumento do tempo de desenvolvimento e a sua adoção, em maior escala, na indústria. Nesse sentido, a Verified Software Initiative (VSI) [HMLS09] materializa-se em uma importante contribuição na direção de fornecer maior maturidade aos métodos formais, visando facilitar e disseminar a sua utilização em software de larga escala. Trata-se de um projeto de longo prazo que convida toda a comunidade da engenharia de software e métodos formais, assim como parceiros industriais, a contribuir com estudos de caso, teorias, técnicas e ferramentas para o desenvolvimento de software confiável e, de forma ideal, livre de erros. O projeto VSI, inicialmente, propôs a implementação de diversos estudos de caso desafiadores, como forma de se criar um repositório de especificações (Verifyed Software Repository [VSI10]) que posteriormente serão utilizadas na validação das novas ferramentas e técnicas desenvolvidas, bem como para verificar e comparar a aplicabilidade de diversos métodos formais. Como exemplos de estudos de casos que vêm sendo desenvolvidos, podemos citar as especificações para o sistema smart card de carteira eletrônica Mondex [SCW00, BY07], de 20 um marcapasso cardíaco [MLF08, GO09] e do Posix [FFW07], um sistema de arquivos em memória flash. 1.2 Desenvolvimento com o Método BSmart Evidencia-se que a presente tese utiliza o método formal B [Abr96] como fundamento para um método de desenvolvimento de aplicações para smart cards na linguagem Java Card, denominado de BSmart. O método B possui uma boa utilização, tanto na academia quanto na indústria, no que concerne à especificação e ao desenvolvimento (com base em refinamentos) de aplicações e partes de sistemas críticos. A base de ferramentas de suporte ao método também é bastante rica, possibilitando a verificação de tipos, geração e verificação de obrigações de prova, animação, geração de código, dentre outras funcionalidades. Além disso, B mostra-se adequado ao desenvolvimento de aplicações Java Card, por elas serem aplicações de pequeno porte e possuírem linguagem simples e enxuta. Observa-se que o trabalho desenvolvido no doutorado teve início na dissertação de mestrado do autor desta tese [Gom07]. Na dissertação, definiu-se a versão inicial do método BSmart, que descreve, em um conjunto de etapas de refinamento em B, o desenvolvimento da aplicação Java Card. Essa primeira versão foi apresentada em [GMD05] e [DGM06]. Ferramentas de apoio ao método também foram implementadas. Destacam-se um gerador dos módulos de refinamento e um tradutor que possibilita a geração da aplicação cartão (applet) a partir do refinamento concreto (implementação B) do desenvolvimento. A ferramenta de tradução também é responsável pela geração de uma API para possibilitar a comunicação da aplicação cliente (host) com o cartão de forma transparente ao usuário. Cumpre destacar que o processo de especificação e refinamento no método BSmart é verificado a cada etapa por meio da resolução das obrigações de prova de cada módulo. No entanto, no trabalho desenvolvido na dissertação, a formalização desse processo não foi realizada. Outro ponto negativo diz respeito à sobrecarga da ferramenta de tradução, que ficou responsável por realizar diversas tarefas além da simples tradução entre as duas linguagens, dessa forma aumentando o risco de introdução de erros na etapa de síntese de código. Dentre elas, é possível destacar a introdução dos tipos compatíveis com Java Card (short, byte, boolean, etc.), a inserção na classe gerada de métodos requeridos pelo framework Java Card e a codificação e decodificação das informações enviadas e recebidas pelas aplicações host e applet. De outro modo, nesta tese, todas essas questões foram introduzidas diretamente durante o processo de especificação e refinamento em B. 21 Ressalta-se que este trabalho concentra-se no processo de especificação e desenvolvimento da aplicação Java Card enquanto software de alto nível. Aspectos da formalização do hardware smart card, de verificação do ambiente de execução ou do bytecode da aplicação cartão não são tratados de forma direta. O trabalho proposto diferencia-se de outras abordagens similares da literatura (discutidas no capítulo 8) por proporcionar o desenvolvimento completo da aplicação Java Card, desde a sua especificação formal de alto nível, passando pelos refinamentos que gradativamente introduzem os aspectos da aplicação Java Card, até a geração de código para os componentes servidor (applet) e cliente (aplicação host). 1.3 Objetivos 1.3.1 Objetivo geral O presente trabalho tem por objetivo contribuir com o método BSmart, uma especialização do método formal B para o desenvolvimento de aplicações smart cards na linguagem Java Card com maior segurança e confiabilidade. Visando atingir esses requisitos de qualidade, a tese prioriza a melhoria do processo de desenvolvimento com método através das seguintes ações: • Introdução dos aspectos relacionados ao ambiente de execução Java Card diretamente nos refinamentos introduzidos pelo método; • Formalização do processo de desenvolvimento; • Desenvolvimento da biblioteca KitSmart que provê módulos B reutilizáveis que auxiliam no processo de verificação e geração de código. 1.3.2 Objetivos específicos Os objetivos específicos do trabalho, apresentados nos tópicos posteriormente mencionados, representam o detalhamento das contribuições desta tese: 1. Formalizar o processo de desenvolvimento com o método BSmart, o que engloba: (a) Especificação formal e verificação do processo de refinamento do método, utilizando-se uma representação genérica de um desenvolvimento seguindo o método BSmart. Esta formalização é apresentada no capítulo 4 através de modelos desenvolvidos na notação B e provados com o auxílio da ferramenta Atelier 22 B [Cle12a]. Observa-se que a representação utilizada tem a vantagem de documentar e verificar o método sem atrelá-lo a um estudo de caso em particular. Desse modo, uma aplicação que pretenda usar o método deve seguir todas as etapas demonstradas, especificando cada módulo requerido e verificando as suas obrigações de prova. (b) Desenvolvimento de uma biblioteca de componentes, denominada de KitSmart, composta por modelos em B das 93 classes/interfaces da API Java Card, dos tipos Java e Java Card e de módulos que especificam tarefas úteis ao desenvolvedor Java Card, mas que não se encontram na API padrão. Além de servir de suporte ao desenvolvimento, fornecendo módulos reutilizáveis, a biblioteca também auxilia o processo de verificação do método permitindo a tipagem correta de variáveis para tipos compatíveis com Java Card e a verificação da chamada de métodos de biblioteca durante os refinamentos. 2. Especificação da tradução do desenvolvimento em B para a aplicação cartão. Esta etapa compreende a descrição da tradução de cada elemento da notação B para o seu correspondente na notação da linguagem Java Card. Com os propósitos de documentar a geração de código e aprimorar o entendimento do processo de tradução, utilizou-se a linguagem ASF+SDF [BKV08] para descrever a gramática de cada linguagem e especificar as regras de transformação entre os elementos da notação B para Java Card. É importante observar que o conjunto de regras não fornecem uma prova da tradução, tendo em vista que, nesse caso, seria necessário a descrição da semântica formal de ambas as linguagens, um processo que requer demasiado esforço, o que extrapolaria o tempo disponível para a conclusão desta tese. 3. Proceder com a reengenharia das ferramentas de suporte ao método. Anteriormente, utilizou-se a ferramenta JBtools [Voi02] para realizar a manipulação de um módulo B. No entanto, essa solução apresentava falhas devido à instabilidade do seu parser e verificador de tipos e a intervenções que foram introduzidas no tradutor B que prejudicavam a confiabilidade do desenvolvimento. Além disso, o JBTools é compatível apenas com a notação clássica da linguagem B, conforme descrito no B-Book [Abr96]. A solução encontrada foi migrar o código para utilizar a API BCompiler [Cle12b], fornecida pela empresa Clearsy, que também desenvolve o Atelier B, uma das principais ferramentas para desenvolvimento em B disponíveis no mercado. Dessa forma, conseguiu-se uma solução estável e alinhada à notação do Atelier B. 4. Desenvolvimento de um estudo de caso de uma aplicação smart card “real” tendo em vista 23 validar o método e o seu suporte de ferramentas. Optou-se pelo passaporte eletrônico, uma importante aplicação que está em uso atualmente no mundo inteiro para permitir a identificação segura de migrantes nos diversos países que adotam o sistema. Para tanto, utilizou-se como base a documentação oficial fornecida pela ICAO [ICA11] e uma versão código-aberto da aplicação desenvolvida na Universidade Radboud [MP10] 1.4 Artigos Publicados Nesta seção descreve-se os trabalhos que foram publicados, desde o início do estudo, em março de 2008, até o presente momento. SBES-2008 [MdMJD+ 08]: A ferramenta Batcave para a verificação formal com o método B A ferramenta Batcave foi desenvolvida por um estudante do grupo de métodos formais da UFRN e tem por objetivo gerar as obrigações de prova de um módulo B em diversas notações. Utilizando essa abordagem, o usuário da ferramenta não fica restrito a um provador específico, de modo que obrigações de prova que poderiam ser trabalhosas de se verificar em um provador, podem ser facilmente verificadas em outra ferramenta. Neste artigo, o autor desta tese colaborou com a integração da ferramenta Batcave à primeira versão do ambiente de desenvolvimento BSmart. ABZ-2008 [DGM08]: BSmart: a tool for the rigorous development of Smart Card applications Apresentou-se a versão inicial da ferramenta BSmart na sessão de ferramentas da conferência Abstract Machine, B and Z (ABZ). FM-2009 [Gom09]: Rigorous Development of Smart Card Applications with the B method Este trabalho foi apresentado no simpósio de doutorado da Conferência Mundial de Métodos Formais (Formal Methods 2009). Uma comissão formada por importantes pesquisadores da área de métodos formais analisou o trabalho e, em conjunto com os demais estudantes que participaram do simpósio, teceu interessantes críticas e sugestões que auxiliaram no aprimoramento desta tese. ABZ-2010 [GDMM10] - Applying the B method for the Rigorous Development of Smart Card Applications Este artigo, apresentado na conferência ABZ de 2010, descreve a for- malização do método BSmart, a geração de código da API para a aplicação host e os módulos 24 da biblioteca KitSmart desenvolvidos até então, o que compreende as máquinas de tipos, e um esboço inicial do modelo da API Java Card. 1.5 Resumo dos Capítulos Apresenta-se abaixo um resumo dos capítulos da tese que se seguem a esta introdução. Capítulo 2: Java Card Este capítulo introduz os componentes do ambiente smart card e a linguagem Java Card. Ao final, demonstra-se o desenvolvimento de uma aplicação Java Card simples, porém completa. A aplicação cartão é desenvolvida tanto em Java Card APDU, versão em que se manipulam diretamente os pacotes para troca de dados entre as aplicações, quanto em Java Card RMI, em que a comunicação é feita por invocação remota de métodos (RMI). No caso do cliente (aplicação host), utilizou-se a API smart Card I/O para realizar a comunicação com a aplicação cartão. Capítulo 3: O Método Formal B Expõe o método B para desenvolvimento formal de aplicações. A linguagem B é extensa, sendo assim, neste capítulo introduz-se apenas a notação B que é relevante para a compreensão deste trabalho. Enfatiza-se também os importantes conceitos de substituição e obrigação de prova. Na última seção do capítulo, é feita uma pequena introdução ao formalismo Event-B e são discutidos os motivos pelos quais continuou-se a desenvolver o método BSmart em B clássico, ao invés de migrá-lo para Event-B. Capítulo 4: O Método BSmart Descreve detalhadamente a formalização do processo de desenvolvimento com o método BSmart, desde a especificação B inicial até o módulo de implementação. O capítulo demonstra também o suporte de ferramentas ao método. Capítulo 5: Tradução para Java Card Apresenta a tradução da linguagem da implementação B para Java Card utilizando o formalismo ASF+SDF [BKV08]. Inicialmente, apresentase a fundamentação do capítulo, o que compreende a especificação de gramáticas em SDF, com exemplos de trechos da gramática B, assim como o uso de ASF para a transformação de código entre linguagens. Posteriormente, detalham-se o conjunto de regras de reescrita que descrevem a tradução de cada produção da gramática de implementação B para a sua produção correspondente em Java Card. Capítulo 6: KitSmart Detalha os componentes da biblioteca KitSmart, que provê módulos B reutilizáveis visando auxiliar o desenvolvimento, o processo de verificação e a geração de código. 25 Capítulo 7: Estudo de Caso O estudo de caso do passaporte eletrônico é descrito neste capítulo. Inicialmente, descreve-se a aplicação e os seus componentes mais importantes (estrutura de dados e protocolos). Posteriormente, a especificação e desenvolvimento da aplicação seguindo o método BSmart é detalhada. Capítulo 8: Trabalhos Relacionados Os trabalhos que estão no escopo do tema da tese são descritos e comparados. São relatados trabalhos em diversas áreas, tais como, verificação, geração de código, políticas de segurança e o estado da arte em ferramentas para desenvolvimento formal de software. Capítulo 9: Considerações Finais Este capítulo tece as considerações finais e apresenta os trabalhos futuros derivados das contribuições da tese. Inclui-se também uma análise da adequação do método B ao desenvolvimento smart card. 26 2 Java Card Inicialmente, destaca-se que um smart card é um dispositivo computacional portátil capaz de executar pequenas aplicações e armazenar informações de forma segura. Fisicamente, um smart card consiste em um pequeno chip, geralmente envolto em um revestimento plástico, que o deixa com a aparência de um cartão de crédito comum. A tecnologia smart card é padronizada pela International Organization of Standartization (ISO) através do seu padrão ISO 7816 que especifica as características físicas, elétricas, mecânicas, os protocolos para transmissão de dados e comunicação com o cartão, dentre outras. Ressalta-se que o chip do smart card é composto por um microprocessador e memórias ROM (Read Only Memory), RAM (Random Access Memory) e EEPROM (Electrical Erasable Programmable Read Only Memory) [HNS+ 02]. A memória ROM é escrita no momento da manufatura do cartão e não pode ser modificada posteriormente. Nesta memória usualmente estão presentes o sistema operacional do cartão e algum aplicativo pré-instalado. A memória RAM é uma memória volátil utilizada para armazenamento temporário de dados. Por fim, a memória EEPROM é responsável por armazenar dados persistentes, mesmo quando o cartão encontra-se sem alimentação de energia [Che00] [HNS+ 02]. É importante observar que Java Card [Che00] é uma das principais linguagens utilizadas para desenvolvimento de aplicações para smart cards. Java Card é uma versão restrita e otimizada da plataforma Java, tendo em vista possibilitar que dispositivos com baixa capacidade de processamento e memória possam armazenar e executar pequenas aplicações. O desenvolvedor que usa Java Card beneficia-se de muitas das características de Java, tais quais portabilidade, linguagem segura quanto a tipos, desenvolvimento orientado a objetos e disponibilidade de ferramentas. Todo esse suporte possibilita uma maior rapidez no ciclo de desenvolvimento, teste e instalação de aplicações. Dessa forma, é possível obter melhores ganhos quanto ao custo e ao tempo na produção de software para smart cards. O principal componente da plataforma Java Card (Figura 2.1) é o seu ambiente de execução, o Java Card Runtime Environment (JCRE), composto por uma Máquina Virtual Java 27 Figura 2.1: Componentes da plataforma Java Card. Adaptado de: [Ort03a] Card (JCVM), uma pequena API e, normalmente, por classes fornecidas pelo fabricante do cartão [Che00]. O JCRE atua como um pequeno sistema operacional, sendo responsável pelo controle do ciclo de vida da aplicação e de aspectos de segurança e gerenciamento de recursos. Em virtude das restrições de processamento e armazenamento dos smart cards atuais, a linguagem e a API Java implementada para Java Card são bastante limitadas. Dentre outros recursos, os tipos de ponto flutuante (float e double) e caractere (char) não estão disponíveis, assim como não há arrays multidimensionais, carga dinâmica de classes e múltiplos fluxos de execução (threads). Como recursos opcionais, a depender do fabricante do cartão, tem-se a implementação do tipo inteiro (int) e de coleta de lixo. Os detalhes dos componentes e restrições da plataforma Java Card podem ser encontrados na especificação oficial da tecnologia [Ora11]. Ressalta-se que este capítulo aborda os aspectos da plataforma Java Card relevantes ao presente trabalho. Inicialmente, na seção 2.1, detalha-se a comunicação entre os componentes do sistema smart card, com foco no protocolo APDU. O processo geral de desenvolvimento de aplicações Java Card é apresentado na seção 2.2. A classe applet, componente que fornece os serviços da aplicação, é descrita na seção 2.3. Por fim, a seção 2.4 apresenta a aplicação host que utiliza os serviços do applet. 2.1 O Modelo de Comunicação Smart Card A aplicação smart card possui uma arquitetura cliente-servidor, sendo o componente servidor interno ao cartão, e o cliente, externo. A aplicação servidora, denominada applet, provê os serviços que são requisitados pela aplicação cliente, conhecida na literatura como aplicação host, que reside em um computador ou terminal eletrônico. Entre essas duas aplicações há um dispositivo de hardware, chamado de leitor ou Card Acceptance Device (CAD), responsável por fornecer energia ao chip do cartão e o meio físico para que o host e o applet possam se 28 comunicar [Ort03a], o que pode ser feito por contato elétrico (cartões por contato) ou por rádio frequência (cartões sem contato). A troca de informações entre as aplicações é feita por meio de um protocolo conhecido como Application Protocol Data Unit (APDU). A norma ISO 7816-4 especifica dois tipos de APDU’s (figura 2.2), um para que a aplicação host possa requisitar algum serviço do cartão (comando APDU) e outro que é enviado no sentido contrário, do cartão para o host, contendo o resultado do processamento do serviço (resposta APDU). Destaca-se que um comando APDU é composto por um cabeçalho obrigatório contendo quatro campos de 1 byte cada. O campo CLA identifica uma classe de instrução (definida pelo desenvolvedor, para acesso a arquivos, etc.), sendo cada instrução específica dentro de uma classe identificada pelo campo INS (normalmente um serviço oferecido). Os campos P1 e P2 podem ser utilizados opcionalmente para fornecer dados de 1 byte cada. Informações adicionais devem ser fornecidas no array data (na figura 2.2, data field). Nesse caso, deve-se informar, no campo Lc, o número de bytes que serão enviados. De forma semelhante, caso seja esperada alguma informação como resposta, o campo Le deve conter seu tamanho esperado. A resposta APDU é dividida em um campo data para se enviar dados na resposta e dois bytes SW1 e SW2, que são sempre retornados contendo o status do processamento do comando. Uma execução sem falhas retorna o status 0x9000 (SW1=90 e SW2=00). Qualquer outro código de status indica que ocorreu uma exceção durante o processamento da requisição. Figura 2.2: Comando e resposta APDU. Fonte: [Ort03a] 2.2 Desenvolvimento de Aplicações Java Card Ressalta-se que uma aplicação Java Card pode ser desenvolvida de duas maneiras. Na primeira, de mais baixo nível, o desenvolvedor tem que lidar diretamente com o aspecto de comunicação smart card por meio do protocolo APDU. Na outra, a comunicação entre a aplicação host e o applet é efetivada através de invocação remota de métodos (RMI), sendo o protocolo APDU abstraído para o desenvolvedor, que tem que lidar apenas com objetos [Ort03b]. Para diferenciar, daqui em diante no texto, os dois tipos de aplicações, denominar-se-á o primeiro de 29 Java Card APDU e o segundo de Java Card RMI. O processo completo de desenvolvimento de uma aplicação Java Card, em suas duas versões, pode ser dividido em quatro etapas, a saber: 1. Desenvolvimento do applet Java Card e de classes Java Card auxiliares necessárias à implementação dos serviços. 2. Compilação e teste (simulação/emulação) do código desenvolvido. 3. Conversão do applet e das classes auxiliares no formato apropriado para a instalação em um cartão. 4. Desenvolvimento da aplicação host. A primeira etapa corresponde ao desenvolvimento da aplicação que será instalada no cartão, denominada de applet, e de outras classes que ela necessite. O applet é o componente mais importante de uma aplicação Java Card, uma vez que nele encontra-se implementado todo o núcleo funcional dessa aplicação. Por este motivo, esta etapa do desenvolvimento será coberta em maior profundidade neste trabalho. A compilação do applet é feita da mesma maneira que qualquer classe Java. A etapa de teste é opcional e pode ser feita por meio de simuladores do ambiente de execução Java Card disponibilizados pela Sun em seu Java Card Development Kit. No kit de desenvolvimento também encontram-se conversores que empacotam o applet e suas classes em um formato para ser instalado no cartão. A última etapa do desenvolvimento corresponde à implementação da aplicação host. Um problema enfrentado pelos desenvolvedores destas aplicações consiste na ampla diversidade de fabricantes de cartões e de CADs, cada um com as suas APIs proprietárias e complexas, o que dificulta a criação de aplicações que possam ser utilizadas entre hardware de diferentes fabricantes sem necessidade de modificação. Com o propósito de contornar esse problema existem APIs como o OpenCard Framework e o PC/SC que permitem que essas aplicações sejam construídas de forma que a comunicação com o applet Java Card seja efetivada de modo interoperável entre dispositivos de diversos fabricantes. Evidencia-se que o desenvolvimento não precisa necessariamente seguir a ordem acima. A aplicação host (etapa 4) pode ser desenvolvida antes ou em paralelo ao applet (etapa 1), desde que se conheça a interface dos serviços fornecidos pelo applet. 30 A seção 2.3 a seguir apresenta o desenvolvimento de aplicações Java Card nas versões APDU e RMI através de um exemplo de aplicação de bilhetagem eletrônica. Posteriormente, na seção 2.4, detalha-se o desenvolvimento da aplicação cliente. 2.3 O applet Java Card O applet Java Card é uma subclasse de javacard.framework.Applet implementada sob as restrições impostas pela especificação Java Card. A conformidade de uma classe com a especificação Java Card é verificada apenas durante a conversão do applet antes da sua instalação no cartão. Nas versões iniciais da plataforma existia apenas um único meio de se desenvolver um applet, no qual o desenvolvedor lidava diretamente com as estruturas do protocolo APDU. No entanto, a partir da versão 2.2, foi disponibilizado um applet que utiliza a comunicação por invocação remota de métodos (RMI), abstraindo os detalhes internos dos protocolos. Por introduzir uma camada adicional, essa última versão costuma ser menos eficiente e, por este motivo, ainda é menos utilizada que a versão APDU. Nas subseções a seguir os modelos de aplicação APDU e RMI são exemplificados através de uma aplicação de bilhetagem eletrônica. O usuário da aplicação dispõe de uma quantidade de créditos em passagens (atributo balance) que são debitados a cada viagem. Observa-se que há três tipos de cartão: gratuidade, passagem inteira e estudante, sendo essa informação armazenada no atributo cardType. Foram implementados três métodos de serviço, são eles addCredit, debit e getCredits. O primeiro adiciona créditos no cartão, o segundo debita o valor de uma unidade de crédito de um cartão que não seja de gratuidade e o método getCredits pode ser utilizado para consultar o saldo atual do cartão. 2.3.1 Applet APDU Nas figuras 2.3, 2.4 e 2.5 apresenta-se o código do applet na versão APDU para a aplicação de bilhetagem eletrônica Transport. É importante destacar a função de alguns métodos herdados da classe Applet e que são geralmente implementados na subclasse, são eles, install, process, select e deselect. Desses, apenas process é de implementação obrigatória, por se tratar de um método abstrato (abstract). No entanto, o método install, devido a sua importância para a aplicação, também é normalmente sobrescrito. A introdução dos métodos select e deselect na subclasse é opcional, uma vez que a implementação herdada é suficiente para permitir a 31 execução da aplicação. O método install é chamado pelo Java Card Runtime Environment (JCRE) apenas na primeira vez em que o applet é executado. Em uma implementação mínima, deve-se criar uma instância da classe applet e registrá-la junto ao ambiente de execução, através da chamada a um dos métodos de registro da classe Applet. Apenas uma instância do applet é registrada, sendo a gerência dessa instância controlada pelo JCRE. Observa-se que o applet permanece em um estado inativo após ser registrado, aguardando até que seja selecionado. p u b l i c c l a s s T r a n s p o r t A p p l e t extends j a v a c a r d . framework . Applet { short balance ; Cards cardType ; p u b l i c s t a t i c f i n a l s h o r t ENTIRE_CARD = ( s h o r t ) 0 x0 ; p u b l i c s t a t i c f i n a l b y t e OP_ADD_CREDIT = ( b y t e ) 0 x20 ; / / ( . . . ) p r i v a t e T r a n s p o r t A p p l e t ( byte [ ] barray , short bOffset , byte bLength ) { super ( ) ; balance = ( short ) 0; c a r d T y p e . s e t C a r d T y p e ( ENTIRE_CARD ) ; } p u b l i c s t a t i c v o i d i n s t a l l ( b y t e [ ] bArray , s h o r t b O f f s e t , b y t e b L e n g t h ) { T r a n s p o r t A p p l e t t r a n s p o r t = new T r a n s p o r t A p p l e t ( bArray , b O f f s e t , b L e n g t h ) ; transport . register (); } p u b l i c v o i d p r o c e s s (APDU apdu ) throws I S O E x c e p t i o n { b y t e b u f f e r [ ] = apdu . g e t B u f f e r ( ) ; / / ( . . . ) s w i t c h ( b u f f e r [ ISO7816 . OFFSET_INS ] ) { c a s e OP_ADD_CREDIT : a d d C r e d i t ( apdu ) ; break ; c a s e OP_DEBIT : d e b i t ( apdu ) ; break ; c a s e OP_GET_CREDITS : g e t C r e d i t s ( apdu ) ; break ; d e f a u l t : I S O E x c e p t i o n . t h r o w I t ( ISO7816 . SW_INS_NOT_SUPPORTED ) ; } } / / ( m é t o d o s de s e r v i ç o . . . ) } Figura 2.3: Um applet Java Card APDU para bilhetagem eletrônica - definição de atributos e métodos herdados da classe Applet. Tendo em vista otimizar o uso da memória e minimizar operações de escrita de dados na memória persistente, é desejável que os objetos utilizados pelo applet sejam instanciados em seu construtor ou no método install diretamente. Uma vez que cada novo objeto ou array instanciado é salvo na memória persistente do cartão, a sua criação através do método install garante que essa operação será efetuada apenas uma vez, sendo o estado desses atributos recuperado em utilizações subsequentes. Outra boa prática é o uso de atributos transientes quando não houver a necessidade da persistência de determinado objeto ou array. Um objeto transiente é criado através de um dos métodos makeTransientArray da classe javacard.framework.JCSystem [Che00]. O método select é chamado sempre que um applet é requisitado para ser selecionado (apdu SELECT FILE). A implementação padrão fornecida pela classe Applet retorna true, dessa forma autorizando o acesso ao applet (a sua seleção para execução). Caso alguma restrição de acesso tenha que ser imposta antes da seleção do applet, ela deve ser feita em select, que irá autorizar 32 ou não a seleção. Por sua vez, o método deselect do applet selecionado (caso haja algum) é chamado sempre que um novo applet requisita ao JCRE a sua seleção. Dessa forma, ele pode ser utilizado para alguma operação de limpeza de memória ou de alteração do estado de algum atributo antes que o applet em execução seja retirado de seleção. Uma vez selecionado, o applet encontra-se pronto para receber as requisições aos seus serviços através do seu método process. Ele recebe do JCRE um objeto APDU, o que permite o acesso ao buffer APDU, um array de bytes contendo os campos do comando que foram enviados na requisição pela aplicação host. Cada elemento do array contém a informação de um campo específico do comando APDU, sendo o índice desses campos no array facilmente acessado através de constantes presentes na interface javacard.framework.ISO7816. Observa-se que, por meio da instrução (INS) do buffer, a requisição é direcionada ao serviço apropriado, que obtém os dados necessários para a sua execução diretamente dos campos p1, p2 ou data contidos no array. Os métodos de serviço do applet são específicos de cada aplicação. É importante observar que os serviços que necessitam acessar os campos do comando APDU devem fazer uma chamada ao método getBuffer de javacard.framework.APDU para obter o array buffer. Um método que recebe algum dado encapsulado no campo data do comando APDU deve invocar o método setIncomingAndReceive para que o JCRE torne esse dado adicional acessível através do buffer APDU recebido. O número de bytes recebidos é retornado pelo método setIncomingAndReceive e pode ser usado para verificar se esse número é igual ao número de bytes que se esperava receber. Como exemplo, na figura 2.4, tem-se a implementação do método addCredit, que deve receber a informação da quantidade de créditos que serão adicionados ao cartão. p u b l i c v o i d a d d C r e d i t (APDU apdu ) { b y t e [ ] b u f f e r = apdu . g e t B u f f e r ( ) ; s h o r t c r = ( s h o r t ) U t i l . m a k e S h o r t ( ( b y t e ) b u f f e r [ ISO7816 . OFFSET_P1 ] , ( b y t e ) b u f f e r [ ISO7816 . OFFSET_P2 ] ) ; s h o r t sum = ( s h o r t ) ( b a l a n c e + c r ) ; short c t = ( short ) cardType . getCardType ( ) ; i f ( ! ( c t ! = GRATUITOUS_CARD ) ) { I S O E x c e p t i o n . t h r o w I t ( EXCEPTIONS . CARD_TYPE_INVALID ) ; } e l s e i f ( ! ( sum <= 3 2 7 6 7 ) ) { I S O E x c e p t i o n . t h r o w I t ( EXCEPTIONS . BALANCE_EXCEEDED ) ; } else i f ( !( cr > 0)) { I S O E x c e p t i o n . t h r o w I t ( EXCEPTIONS . NEGATIVE_CREDIT ) ; } else { b a l a n c e = sum ; } } Figura 2.4: Método de serviço addCredit do applet Transport. 33 Caso um método precise enviar alguma informação para a aplicação host ele deve, inicialmente, invocar o método setOutgoing, responsável por notificar o JCRE que algo será enviado no campo data da resposta APDU. O método setOutgoing também retorna o tamanho do dado que a aplicação host espera receber. Posteriormente, o método setOutgoingLength deve ser chamado para informar o número de bytes a serem enviados. Por fim, deve-se inserir a informação no buffer apdu. No método getCredits do exemplo (figura 2.5), a variável contendo a informação da quantidade de créditos restantes no cartão é inserida no buffer apdu através do método setShort, da classe javacard.framework.Util. Finalmente, através da chamada a sendBytes, a resposta é enviada. p u b l i c v o i d g e t C r e d i t s (APDU apdu ) { b y t e [ ] b u f f e r = apdu . g e t B u f f e r ( ) ; s h o r t r e s = amount . g e t S h o r t V a l u e ( ) ; s h o r t l e = apdu . s e t O u t g o i n g ( ) ; apdu . s e t O u t g o i n g L e n g t h ( ( s h o r t ) 2 ) ; U t i l . s e t S h o r t ( buffer , ( short ) 0 , ( short ) res ) ; apdu . s e n d B y t e s ( ( s h o r t ) 0 , ( s h o r t ) 2 ) ; } Figura 2.5: Método de serviço getCredits do applet Transport. 2.3.2 Applet RMI Destaca-se que a API Java Card para Invocação Remota de Métodos (RMI) foi introduzida somente na especificação Java Card 2.2, consistindo em um subconjunto da API RMI de Java e classes RMI específicas para Java Card. No modelo RMI, a aplicação servidora (o applet Java Card) cria e torna acessíveis objetos que podem ser acessados remotamente através de uma interface pública. A aplicação cliente pode obter as referências a esses objetos e então invocar os seus métodos [Ort03b]. O desenvolvimento de um applet Java Card RMI compreende a especificação dos serviços providos pela aplicação remota como uma interface Java, a implementação dessa interface e o desenvolvimento do applet (subclasse de Applet) e demais classes auxiliares. A interface remota (figura 2.6) especifica os serviços que serão fornecidos pelo applet para a aplicação host. Para tanto, ela deve estender a interface java.rmi.Remote. Observa-se que os métodos da interface remota são exatamente os mesmos encontrados no applet APDU. A diferença é que, agora, esses não serão mais implementados dentro da classe applet e que a sua assinatura foi modificada para incluir o lançamento de exceções. A RemoteException é exigência da interface Remote, sendo utilizada para reportar erros que por ventura ocorram 34 quando da execução de uma chamada remota de método. Já a especificação da UserException foi incluída para que se possa reportar os erros específicos da lógica de negócio da aplicação. p u b l i c i n t e r f a c e T r a n s p o r t R e m o t e I n t e r f a c e e x t e n d s Remote { p u b l i c v o i d a d d C r e d i t ( s h o r t c r ) throws R e m o t e E x c e p t i o n , U s e r E x c e p t i o n ; p u b l i c v o i d d e b i t ( ) throws R e m o t e E x c e p t i o n , U s e r E x c e p t i o n ; p u b l i c s h o r t g e t C r e d i t s ( ) throws R e m o t e E x c e p t i o n , U s e r E x c e p t i o n ; } Figura 2.6: Interface remota do Applet RMI A implementação dos métodos da interface remota é feita pela classe TransportRemoteImplementation (figura 2.7), subclasse de javacard.framework.service.CardRemoteObject. Herdar de CardRemoteObject tem o efeito de exportar automaticamente o objeto remoto, possibilitando o acesso a ele e aos seus métodos pela aplicação host. public class TransportRemoteImplementation e x t e n d s C a r d R e m o t e O b j e c t implements T r a n s p o r t R e m o t e I n t e r f a c e { private short balance ; Cards cardType ; // ( ...) p u b l i c v o i d a d d C r e d i t ( s h o r t c r ) throws R e m o t e E x c e p t i o n , U s e r E x c e p t i o n { short c t = ( short ) cardType . getCardType ( ) ; i f ( ! ( c t ! = GRATUITOUS_CARD ) ) { U s e r E x c e p t i o n . t h r o w I t ( EXCEPTIONS . CARD_TYPE_INVALID ) ; } e l s e i f ( ! ( sum <= 3 2 7 6 7 ) ) { U s e r E x c e p t i o n . t h r o w I t ( EXCEPTIONS . BALANCE_EXCEEDED ) ; } else i f ( !( cr > 0)) { U s e r E x c e p t i o n . t h r o w I t ( EXCEPTIONS . NEGATIVE_CREDIT ) ; } else { balance = ( short ) ( balance + cr ) ; } } // ( ...) p u b l i c s h o r t g e t C r e d i t s ( ) throws R e m o t e E x c e p t i o n , U s e r E x c e p t i o n { return balance ; } } Figura 2.7: Implementação dos serviços do applet RMI Por fim, a figura 2.8 apresenta o código do applet Java Card na versão RMI. A estrutura do applet não é modificada com relação ao APDU. A principal diferença é a instanciação de classes específicas para implementação da comunicação por RMI. A classe Dispatcher registra um serviço remoto e encaminha todo comando APDU para a classe de serviço registrada. Neste caso, temos a classe de serviço RMIService, que então recebe este APDU e o traduz em chamadas de métodos do objeto remoto passado em seu construtor [Ort03b]. 35 p u b l i c c l a s s T r a n s p o r t A p p l e t extends j a v a c a r d . framework . Applet { private Dispatcher dispatcher ; private RemoteService s e r v i c e ; p r i v a t e Remote r e m o t e I m p l ; p r i v a t e T r a n s p o r t A p p l e t ( b y t e [ ] bArray , s h o r t b O f f s e t , b y t e b L e n g t h ) { super ( ) ; r e m o t e I m p l = new T r a n s p o r t R e m o t e I m p l e m e n t a t i o n ( ) ; s e r v i c e = new R M I S e r v i c e ( r e m o t e I m p l ) ; d i s p a t c h e r = new D i s p a t c h e r ( ( s h o r t ) 1 ) ; d i s p a t c h e r . a d d S e r v i c e ( s e r v i c e , D i s p a t c h e r . PROCESS_COMMAND ) ; } p u b l i c s t a t i c v o i d i n s t a l l ( b y t e [ ] bArray , s h o r t b O f f s e t , b y t e b L e n g t h ) { T r a n s p o r t A p p l e t t r a n s p o r t = new T r a n s p o r t A p p l e t ( bArray , b O f f s e t , b L e n g t h ) ; transport . register (); } p u b l i c v o i d p r o c e s s (APDU apdu ) throws I S O E x c e p t i o n { d i s p a t c h e r . p r o c e s s ( apdu ) ; } } Figura 2.8: Implementação da classe applet na versão RMI 2.4 Aplicação Host A compatibilidade entre um applet Java Card e um cartão será obtida desde que o applet seja implementado de acordo com a versão da plataforma suportada pelo cartão. Todavia, a ampla interoperabilidade entre aplicações Java Card será alcançada apenas se todo o sistema smart card for compatível, incluindo cartões, leitores (CAD), protocolos e aplicação host. Nesse sentido, surgiram algumas iniciativas formadas pela associação de fabricantes e interessados no mercado smart card, tais como a PS/SC [PC/09] e a Global Platform [Glo09]. O grupo PC/SC, composto por empresas como Microsoft, IBM, HP, Gemplus, dentre outras, possui interesse na compatibilidade do hardware smart card com computadores pessoais. Por sua vez, Global Platform é uma iniciativa da empresa Visa, com o objetivo de prover soluções seguras e interoperáveis para cartões smart card com múltiplas aplicações e terminais eletrônicos de pagamento [HNS+ 02]. Do ponto de vista do desenvolvedor, ambas as plataformas são soluções completas para inicializar os leitores e cartões e para gerenciar a comunicação por meio do protocolo APDU, levando em consideração aspectos de segurança para o sistema como um todo. A API Smart Card I/O do Java 6 foi uma contribuição recente para permitir o desenvolvimento de uma aplicação host simples, mas que atende à necessidade da maioria das aplicações e é compatível com leitores (CAD) PS/SC. Na Figura 2.9, tem-se um exemplo de aplicação host, implementada em Smart Card I/O, para comunicar-se com a aplicação de bilhetagem eletrônica TransportApplet. 36 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class TransportHost { p r i v a t e s t a t i c CommandAPDU SELECT_APDU = new CommandAPDU( 0 x00 , 0 xa4 , 0 x04 , 0 x00 , / / T r a n s p o r t AID : new b y t e [ ] { ( b y t e ) 0 x0A , ( b y t e ) 0 X0B , ( b y t e ) 0 X0C , ( b y t e ) 0X0D , ( b y t e ) 0 X0E , ( b y t e ) 0 X1F } ) ; p u b l i c s t a t i c v o i d main ( ) { TerminalFactory factory = TerminalFactory . getDefault ( ) ; List <CardTerminal > t e r m i n a l s ; try { terminals = factory . terminals ( ) . l i s t (); System . o u t . p r i n t l n ( " T e r m i n a l s a v a i l a b l e : " + t e r m i n a l s ) ; CardTerminal t e r m i n a l = t e r m i n a l s . get ( 0 ) ; / / e s t a b l i s h a c on ne ct io n with the card Card c a r d = t e r m i n a l . c o n n e c t ( "T=0 " ) ; System . o u t . p r i n t l n ( " c a r d : " + c a r d ) ; CardChannel channel = card . getBasicChannel ( ) ; / / e n v i a o APDU SELECT p a r a s e l e c i o n a r o a p p l e t T r a n s p o r t . ResponseAPDU r a = c h a n n e l . t r a n s m i t ( SELECT_APDU ) ; / / E n v i a comando apdu p a r a chamar o s e r v i ç o g e t C r e d i t s : CLA , INS , P1 , P2 , LE CommandAPDU g e t _ c r _ c m d = new CommandAPDU( 0 x80 , 0 x50 , 0 , 0 , 2 ) ; ResponseAPDU r e s p = c h a n n e l . t r a n s m i t ( g e t _ c r _ c m d ) ; System . o u t . p r i n t l n ( " Q u a n t i d a d e de c r é d i t o s : " + a r r a y T o H e x ( r e s p . g e t B y t e s ( ) ) ) ; card . disconnect ( f a l s e ) ; } catch ( CardException e ) { e . printStackTrace ( ) ; } } } Figura 2.9: Um exemplo simples de aplicação host para o applet Transport. O primeiro passo em direção à comunicação com a aplicação cartão é obter a referência do leitor onde o cartão está inserido (linhas 11-13). Para tanto, deve-se utilizar a classe TerminalFactory que busca e retorna a lista dos leitores conectados ao terminal eletrônico ou computador. A partir da lista obteve-se a referência para o primeiro leitor conectado (terminals.get(0)). O segundo passo consiste em estabelecer a conexão com o cartão através do método connect da referência ao leitor (linha 16). Esse método necessita da informação do protocolo de transporte (T=0 ou T=1) que o cartão utiliza. Uma vez conectado ao cartão, deve-se obter um canal de comunicação com ele, pela chamada ao método card.getBasicChannel do objeto que representa o cartão (linha 18). A partir daí, é possível enviar e receber comandos APDU. Um comando APDU pode então ser criado utilizando-se um dos construtores da classe CommandAPDU. Inicialmente, enviou-se um comando APDU select para selecionar o applet Transport (linha 21). Para tanto, utilizou-se o método transmit da classe CardChannel, responsável por enviar o comando e retornar um objeto do tipo ResponseAPDU contendo o resultado do processamento do comando. O segundo comando enviado correponde à chamada ao serviço getCredits (linha 23). Uma vez que getCredits retorna dados na resposta (a quantidade de créditos armazenada), é necessário invocar o método getBytes da referência à resposta para obter 37 um array de bytes contendo essa informação (linha 26). O exemplo simplificado de aplicação host acima demonstra os requisitos mínimos para se comunicar com o applet por meio da API Smart Card I/O. Entretanto, como pode-se perceber, nessa aplicação o código de lógica de negócio encontra-se entrelaçado com o código para comunicação de baixo-nível com o cartão. No capítulo 4 apresenta-se uma abordagem para gerar a aplicação host a partir da API da sua especificação formal que abstrai para o desenvolvedor todos os aspectos de comunicação de baixo nível. Neste capítulo apresentou-se a plataforma Java Card para desenvolvimento de aplicações para smart cards. Foram descritos os componentes básicos do sistema smart card e a linguagem Java Card. O capítulo concentrou-se no desenvolvimento da aplicação, uma vez que o objetivo maior do trabalho é possibilitar o desenvolvimento dessas aplicações através de uma especialização do método formal B. O próximo capítulo fornece uma visão geral do método formal B, com ênfase na sua aplicação ao desenvolvimento de software, desde a especificação até a geração de código. 38 3 O Método Formal B No contexto deste trabalho, cumpre destacar que os aspectos centrais do desenvolvimento de software são tratados pelo método B [Abr96] através de uma abordagem dirigida por modelos que possibilita a especificação, refinamento e geração de código. Essas etapas do desenvolvimento são conduzidas através de três tipos de módulos, nomeados, respectivamente, de máquina (machine), refinamento (refinement) e implementação (implementation). Evidencia-se que o desenvolvimento em B tem início no módulo de máquina, que é uma linguagem de especificação de uma máquina de estados: variáveis compõem o estado, e operações definem as transições. As propriedades funcionais de alto-nível do sistema são descritas por uma notação composta por lógica de predicados, conjuntos, relações, funções e aritmética inteira. Essas propriedades são especificadas no invariante da máquina, um predicado que estabelece os possíveis estados que ela pode alcançar através da tipagem e da restrição aos valores que as suas variáveis de estado podem assumir. A especificação inicial estabelece os requisitos funcionais globais da aplicação. No entanto, devido a possuir diversas construções não-determinísticas, úteis à especificação abstrata, esse modelo não é diretamente executável. O desenvolvimento em B progride na direção de um modelo imperativo (determinístico) da especificação inicial através da introdução de refinamentos, cuja execução realiza o modelo de especificação inicial. Os refinamentos também podem complementar a especificação com novos requisitos funcionais, desde que eles não violem as propriedades previamente especificadas, o que pode ser verificado através de ferramentas. Ressalta-se que é possível introduzir vários níveis de refinamento tendo em vista diminuir o comportamento não-determinístico em pequenos passos, bem como para facilitar o esforço de verificação do desenvolvimento. O último estágio de refinamento, conhecido como implementação, é descrito em uma linguagem imperativa de comandos que pode ser traduzida em linguagens de programação, tais quais C, Ada, Java ou Java Card, sendo essa última um resultado desta tese. É importante ressaltar que todo módulo B, seja ele uma máquina, refinamento ou imple- 39 mentação, é composto por diferentes cláusulas, especificadas através de uma notação denominada de Abstract Machine Notation (AMN). Essa notação estabelece o papel de cada cláusula, possibilitando um melhor entendimento do seu funcionamento, bem como facilitando o aprendizado do formalismo. As diversas cláusulas relacionam-se a um aspecto específico em um módulo, como a descrição de variáveis e a especificação do invariante. Nas seções a seguir será enfatizado apenas o subconjunto da notação de máquina abstrata que é relevante para a compreensão deste trabalho. Uma apresentação mais completa e detalhada pode ser encontrada em Abrial [Abr96]. Ressalta-se que a verificação suportada por ferramentas é o componente que torna o método B um formalismo importante para a especificação e o desenvolvimento de software. Cada passo do desenvolvimento em B é seguido por uma etapa de verificação formal através da verificação de obrigações de prova. O resultado positivo dessa verificação garante a consistência das propriedades estabelecidas para o componente, bem como asseguram a correção das transições de estado na inicialização e na aplicação das operações. Dessa forma, ao final do processo de desenvolvimento utilizando-se o método B, assegura-se que o código gerado estará correto, de acordo com a sua especificação abstrata inicial, assumindo-se a correção da ferramenta geradora de código. Evidencia-se que outro incentivo relevante para o emprego do método formal B é a sua base de ferramentas acadêmicas e comerciais. O Atelier B [Cle12a] é um exemplo importante de uma ferramenta comercial que fornece um ambiente completo para o desenvolvimento em B, composto por verificador de tipos, gerador de obrigações de prova, provador, gerador de código, dentre outras. A empresa Clearsy 1 , que desenvolve e comercializa o Atelier B, distribui uma versão gratuita desse software para uso acadêmico, uma iniciativa que pode ajudar no crescimento da utilização do formalismo B. Destacam-se diversas iniciativas acadêmicas de suporte ao método. Nessa linha, tem-se o Pro-B [LBBP10], um programa para a animação (um tipo de execução de um módulo B) e verificação de modelos em B e o Brillant [MC10], um conjunto de programas open source que inclui muitas das ferramentas fornecidas pelo Atelier B. O estado da arte em ferramentas de suporte ao método B é discutido em maiores detalhes no capítulo 8 (seção 8.1). Destaca-se que a utilização do formalismo B, tanto na indústria quanto na academia, vem crescendo como resultado do seu suporte de ferramentas e da sua adequação à especificação e desenvolvimento de software. A indústria ferroviária [BBFM99] [LS07] é um exemplo clássico de uma área que se beneficia do emprego de B. No capítulo de trabalhos relacionados (cap. 8) 1 Sítio Web da empresa Clearsy: http://www.clearsy.com. Último acesso em 21 jun. 2012. 40 será discutido o uso de B para a especificação e verificação de software smart card. Dividiu-se o presente capítulo como descrito a seguir. A seção 3.1 aborda os principais aspectos relacionados à especificação funcional como uma máquina abstrata B. Nela introduz-se parte da notação B e as noções fundamentais de obrigação de prova e substituição. A seção 3.2 apresenta o refinamento, detalhando suas restrições, obrigações de prova e o módulo de implementação. Por fim, na seção 3.3, discute-se o formalismo Event-B e os motivos que levaram a continuação do desenvolvimento deste trabalho em B. 3.1 Especificação Abstrata (Máquina B) Uma máquina abstrata B corresponde à especificação funcional de um sistema ou programa. Sendo assim, a notação de máquina é composta por diversas construções que são úteis à especificação, tais como, paralelismo, escolha não-determinística, operações com pré-condição, dentre outras. Esta seção apresenta os componentes principais desta notação e introduz as noções de substituição e obrigação de prova, essenciais à verificação da consistência de um módulo B. Esses conceitos, assim como parte da AMN a ser abordada na presente seção, também se aplicam aos módulos B de refinamento e implementação a serem abordados na seção 3.2. A figura 3.1 apresenta a estrutura de uma máquina B simples. A cláusula MACHINE é utilizada para nomear a especificação. Opcionalmente, é possível introduzir um ou mais parâmetros seguindo-se ao nome da máquina, entre parênteses (M(pm )). A cada parâmetros deve obrigatoriamente ser fornecido um tipo e, de forma opcional, podem ser definidas restrições aos seus valores na cláusula CONSTRAINTS. Em uma máquina, também é possível a introdução de valores contantes, que devem ser nomeados na cláusula CONSTANTS e tipados na cláusula PROPERTIES. Cada constante deve receber um valor concreto definitivo apenas na cláusula VALUES do módulo de implementação. De modo semelhante, os identificadores das variáveis da máquina devem ser fornecidos em VARIABLES. O tipo e a restrição ao valor de uma variável, assim como a sua relação com outras variáveis, conjuntos e constantes declaradas, são especificados em um predicado na cláusula INVARIANT. Destaca-se que o invariante é um dos principais componente da máquina. O predicado na cláusula INVARIANT, em conjunto com as operações da máquina, estabelecem os requisitos funcionais do sistema especificado. Esse componente também é a base para a verificação da máquina nos componentes relacionados à mudança de estado, o que pode ocorrer na inicialização das variáveis, que deve ser feita na cláusula INITIALISATION, e após a aplicação de uma operação. 41 MACHINE M(pm ) CONSTRAINTS Const(pm ) SETS T CONSTANTS c PROPERTIES Prop(c) VARIABLES v INVARIANT Inv(v) INITIALISATION Init(v) OPERATIONS r ←− op(p) = b PRE Pre(v, p) THEN S(v, p, r) END END Figura 3.1: Um modelo de uma máquina B simples. As operações definem o comportamento dinâmico da máquina após a inicialização. Por intermédio das operações, o estado da máquina pode ser alterado de acordo com as restrições impostas pelo predicado invariante, ou simplesmente consultado, o que não gera mudança de estado. A sentença PRE-THEN-END no corpo da operação definida no modelo da figura 3.1 é uma construção chamada substituição generalizada, que estabelece, no predicado da parte PRE, as condições para a correta aplicação da operação. A seção 3.1.1 detalha a noção de substituição e a sua importância para o processo de verificação em B. Ressalta-se que a especificação B geralmente é composta por outras cláusulas. A seguir, apresenta-se um resumo de importantes cláusulas que são comumente utilizadas em uma máquina B. Cláusula de definição de Conjuntos (SETS) Permite a definição de conjuntos que podem ser adiados (deferred set) ou enumerados. Um conjunto é adiado quando, na sua declaração, é fornecido apenas o seu nome. Com isso, assume-se que o conjunto é não-vazio e finito. Ele é denominado “adiado” devido aos seus elementos serem fornecidos apenas no módulo de implementação. Por outro lado, na declaração de um conjunto enumerado são fornecidos explicitamente os seus valores, que devem ser distintos entre si. Após a sua definição, um conjunto pode ser utilizado na tipagem de variáveis e constantes. 42 Cláusula de variáveis concretas (CONCRETE_VARIABLES) Estabelece os componen- tes do estado que não estão sujeitos ao refinamento. A declaração de variáveis concretas é restrita apenas aos tipos escalares (inteiros, BOOL, conjuntos adiados e enumerados) e a funções totais. Às variáveis concretas são associados tipo e/ou restrições na cláusula INVARIANT, do mesmo modo que aquelas declaradas na cláusula VARIABLES. Cláusulas de composição de máquinas Tendo em vista diminuir a complexidade da espe- cificação e melhor gerenciar as suas obrigações de prova, geralmente uma especificação B é composta por um conjunto de máquinas que realizam cada uma parte do esforço de especificação. É possível promover a composição de máquinas através das cláusulas descritas a seguir. SEES Um módulo B pode incluir (ver) qualquer outra máquina que lhe forneça informação útil. Os conjuntos e constantes de uma máquina vista podem ser lidos, mas não modificados, na máquina que a inclui. A composição com SEES não permite nenhum acesso em relação a variáveis e operações. Um refinamento deve incluir todas as máquinas vistas pelo módulo abstrato sendo refinado. INCLUDES Além dos conjuntos e constantes (acessíveis pela cláusula SEES), as operações de uma máquina incluída podem ser utilizadas. As variáveis de uma máquina incluída podem ser modificadas dentro da máquina que a inclui. No entanto, essa alteração pode ser feita apenas através das operações da máquina incluída, garantindo assim que o invariante desta máquina não seja violado. É gerada uma obrigação de prova quando da instanciação de uma máquina incluída para se verificar se as suas pré-condições são satisfeitas pela máquina que a inclui. Se múltiplas instâncias da mesma máquina necessitam ser acrescentadas, deve-se renomear cada instância com um prefixo seguido de um ponto antes do nome da máquina. PROMOTES Permite que uma ou mais operações de uma máquina incluída (INCLUDES) ou importada (IMPORTS) tornem-se operações da máquina que a inclui (ou importa). A importação de máquinas aplica-se apenas ao módulo B de implementação, descrito na seção 3.2.2. As operações promovidas, mesmo definidas em outro módulo, passam a fazer parte do conjunto de operações da máquina que inclui. Para promover uma operação é necessário apenas colocar o seu nome na cláusula PROMOTES. Ressalta-se que é possível chamar uma operação promovida dentro de uma operação ou da inicialização da máquina que a promoveu, o que não é permitido a operações da própria máquina. 43 EXTENDS Equivale a incluir (INCLUDES) ou importar (IMPORTS) uma máquina e promover (PROMOTES) todas as operações da máquina incluída na máquina que a inclui. 3.1.1 Substituições e obrigações de prova Conforme apresentado na seção anterior, uma máquina B possui um estado determinado pelas restrições impostas no invariante. Para que esse estado mantenha-se consistente, deve-se assegurar que em uma mudança de estado, a partir de um estado atual válido, ou seja, que atende às restrições do invariante, deve-se chegar a um estado posterior também válido. Ressalta-se que as mudanças de estado em uma operação são definidas através de um conjunto de substituições. Dessa forma, as substituições e o seu efeito perante o estado da máquina (sobre o invariante) são a base para a geração e a verificação de obrigações de prova em B. Nesta seção apresenta-se algumas dessas substituições, que permitem especificar a atualização do estado de uma variável a partir de uma expressão, a aplicação não-determinística de substituições, a aplicação de substituições condicionadas ao valor de um predicado, dentre outras. A notação [S]P descreve a aplicação de uma substituição S sobre um predicado P que representa o estado após a aplicação de S. Essa transformação será válida (verdadeira) em um determinado estado, quando ao se executar S sobre esse estado, atinge-se um estado final onde P é válido [Sch01]. Observa-se que a notação S[P] é utilizada na definição das regras que regem o efeito da aplicação de cada substituição presente em B, assim como na definição das obrigações de prova em um módulo. Inicialmente, consideremos o caso da substituição simples, ou becomes equal to. Essa substituição possui a forma x := E, sendo x uma variável de estado e E uma expressão. O efeito dessa substituição é atualizar o estado de x pela expressão E. Para verificar que essa atualização é válida em um dado estado P, deve-se verificar: [x := E]P, em que todas as ocorrências livres da variável x em P são substituídas por E. Por exemplo, considere a declaração de uma variável nomeada number, cujo tipo, especificado no invariante, é o tipo B inteiro (Z) restrito ao intervalo de 1 a 10. Suponha que number seja inicializada com o valor 0 (zero). Em B, teríamos a representação abaixo: (...) INVARIANT number ∈ Z ∧ number ∈ 1..10 INITIALISATION number := 0 (...) 44 Utilizando a regra [S]P para a substituição simples, é possível verificar se a inicialização proposta no exemplo é correta através da substituição do valor da variável number por todas as ocorrências livres de number no predicado invariante. Abaixo, demonstra-se a fórmula lógica resultante. (i) [number := 0](number ∈ Z ∧ number ∈ 1..10) (ii) 0 ∈ Z ∧ 0 ∈ 1..10 Através da análise do predicado após as substituições (ii), conclui-se que a inicialização proposta não é correta, de acordo com o invariante da máquina, uma vez que 0 (zero) não pertence ao intervalo de 1 a 10 (0 ∈ / 1..10). O exemplo, apesar de simples, ilustra a verificação da inicialização de uma máquina. Basicamente, em uma máquina B, deve-se verificar se a inicialização estabelece as propriedades descritas no invariante e, para cada operação, se ela preserva a validade do invariante. Obrigação de prova para a inicialização A substituição de inicialização estabelece o va- lor inicial das variáveis da máquina. Haja vista que esses valores definem o estado inicial da especificação, deve-se gerar obrigações de prova com o objetivo de se verificar a validade da inicialização de acordo com o invariante. A regra geral da obrigação de prova para a inicialização é definida por: [S]I Na regra, S é a substituição empregada no corpo da cláusula INITIALISATION para inicializar as variáveis e I é o predicado invariante da máquina. Obrigação de prova para as operações Uma operação é o componente da máquina que pode modificar o seu estado após a inicialização. A obrigação de prova de uma operação é definida pela regra: I ⇒ [S]I Nesta obrigação de prova, I representa o invariante da máquina e S é a substituição presente no corpo da operação. Ela estabelece que, supondo a correção do invariante antes da aplicação de uma operação, ele deve continuar válido após essa aplicação. De outro modo, uma operação deve preservar o invariante, assegurando, dessa forma, a correção do estado da máquina. Ressalta-se que, geralmente, as operações de uma máquina abstrata possuem uma substitui- 45 ção de pré-condição que estabelece as restrições que devem ser respeitadas de modo a garantir a aplicação correta da operação, de acordo com as restrições impostas pelo invariante. Na próxima seção, detalha-se essa e outras importantes substituições generalizadas que podem ser utilizadas em uma máquina B. 3.1.2 Substituições generalizadas A noção de substituição simples pode ser estendida para substituições mais elaboradas. O conjunto dessas novas substituições fornece ao especificador uma ferramenta poderosa para descrição do comportamento da especificação. Na presente seção podem ser vistas importantes substituições generalizadas. O termo “substituição generalizada” expressa a ideia de que a aplicabilidade de uma substituição pode ser expandida ou restrita para se descrever e se verificar diversos novos comportamentos. É possível, por exemplo, impor restrições à aplicação de uma substituição e especificar a execução paralela de substituições. Pré-condição A pré-condição especifica as propriedades que devem ser satisfeitas para a correta aplicação de uma operação. Seja P a pré-condição da operação, S a substituição em seu corpo, e I um predicado (normalmente o invariante da máquina), esta substituição pode ser formalmente descrita como: [PRE P THEN S END]I ⇔ P ∧ [S]I É importante observar que a validade de [S]I é condicionada ao predicado da pré-condição ser satisfeito. Caso a pré-condição não possa ser garantida, então não há garantia de que a máquina será levada para um estado válido após a aplicação da operação. Na fórmula abaixo, a obrigação de prova para uma operação é estendida, agora levando-se em conta que ela possui pré-condição. I ∧ P ⇒ [PRE P THEN S END]I I ∧ P ⇒ P ∧ [S]I, que pode ser reduzida para: I ∧ P ⇒ [S]I Informalmente, a fórmula acima afirma que, supondo a validade do invariante (a máquina encontra-se em um estado permitido) antes da aplicação da operação, e que ela seja aplicada 46 respeitando-se as restrições descritas na pré-condição, então o estado posterior à sua aplicação deve ser também válido, em respeito ao invariante da máquina. Condicional A substituição condicional é formalmente descrita por: [IF P THEN S ELSE T END]R ⇔ (P ⇒ [S]R) ∧ (¬P ⇒ [T ]R) A aplicação da substituição S ou da substituição T é condicionada pela satisfação do predicado P. Nos casos em que P é verdadeiro, então S é a substituição efetuada, de outro modo, T será aplicada. A substituição condicional também possui uma forma curta, sem a parte ELSE. Nesse caso, apenas a substituição S é condicionada pelo predicado P. Sem efeito (skip) A substituição “sem efeito”, ou simplesmente skip, apesar do seu nome, possui um papel importante. Ela é útil na definição de novas substituições, como no caso da substituição condicional que não possui a parte ELSE, além de ser um meio para se adiar a especificação do comportamento de uma operação. Assim, o comportamento é especificado somente posteriormente, em um refinamento da operação. A substituição skip é formalizada pela regra: [skip]I ⇔ I Como ilustração, a substituição condicional, quando utilizada sem a parte ELSE, pode ser definida do mesmo modo que a sua versão completa, apenas adicionando-se a substituição skip na parte ELSE. IF P THEN S ELSE skip END ≡ IF P THEN S END Paralela As substituições sob o escopo da substituição paralela são aplicadas “simultane- amente”. Ou seja, não é possível supor as noções de ordem e tempo na aplicação de cada substituição individualmente. Esta substituição normalmente é utilizada na especificação de mais de uma substituição em uma operação abstrata ou para se inicializar múltiplas variáveis utilizando-se uma combinação de substituições simples. Não há regra específica para se obter a obrigação de prova para substituições sob o escopo da substituição paralela. Dessa forma, deve-se reescrever as substituições envolvidas até que o paralelismo seja removido [Sch01]. As regras básicas para a reescrita, sendo S e T substituições, x e y variáveis e E e F expressões, são: 47 skip: S k skip = S Comutatividade: SkT =T kS Substituição múltipla em paralelo: (x1 ,..., xn := E1 ,..., En ) k (y1 ,..., ym := F1 ,..., Fm ) = x1 ,..., xn , y1 ,..., ym := E1 ,..., En , F1 ,..., Fm A seguir descreve-se como exemplo as regras para as substituições de pré-condição e condicional quando em paralelo com outra substituição. As regras para as demais substituições generalizadas, por serem semelhantes, foram omitidas. (PRE P THEN S END) k T = PRE P THEN S k T END (IF E THEN S1 ELSE S2 END) k T = IF E THEN S1 k T ELSE S2 k T END Escolha A substituição de escolha (bounded choice) permite descrever mais de um com- portamento válido para uma operação. O comportamento que será efetivamente implementado deve ser escolhido pelo especificador em um refinamento da operação. Formalmente, tem-se: [CHOICE S OR T END]I ⇔ [S]I ∧ [T ]I A conjunção de substituições na fórmula de escolha pode ser informalmente interpretada, afirmando-se: “Não importa qual substituição será escolhida pelo especificador, desde que ela respeite e preserve a correção do invariante”. Essa noção pode ser estendida para qualquer número de substituições em uma escolha. Any Permite introduzir definições locais de variáveis em uma substituição. Seja v uma lista de nomes de variáveis e Q um predicado contendo o tipo e as condições que devem ser satisfeitas para a utilização das variáveis introduzidas. A substituição S estabelece o predicado P, sempre que for possível escolher uma valoração para as variáveis v, sob as restrições de Q, que tornem a substituição de S em P válida. [ANY v WHERE Q THEN S END]P ⇔ ∀v.Q(v) ⇒ ([S]P) 48 3.2 Refinamento O refinamento é o módulo B utilizado para se redefinir a descrição do estado (dados) e do comportamento (operações) de uma máquina abstrata ou outro refinamento prévio. Ambos os refinamentos de dados e operações podem ser aplicados visando reduzir, de forma gradual no desenvolvimento, o não-determinismo de construções abstratas na direção de uma representação concreta da especificação. No último nível de refinamento, a implementação, deve-se utilizar uma notação puramente determinística, fato esse que possibilita a tradução do desenvolvimento para uma linguagem de programação. Outra aplicação deste tipo de refinamento é o gerenciamento da complexidade das obrigações de prova em um módulo B. Usualmente, uma cadeia de refinamentos é desenvolvida, permitindo que pequenas mudanças sejam introduzidas a cada refinamento, reduzindo progressivamente a complexidade da especificação. A figura 3.2 apresenta um esquema genérico de um módulo B de refinamento. O refinamento é nomeado na cláusula REFINEMENT. O nome do módulo que está sendo refinado deve ser fornecido na cláusula REFINES. É possível introduzir novas variáveis na cláusula VARIABLES do refinamento. As variáveis devem receber, no invariante, uma expressão de atribuição de tipo e restrições adicionais ao seu estado, assim como é feito para as variáveis abstratas. Entretanto, o invariante do refinamento possui um papel adicional. Nele deve-se relacionar o estado concreto do refinamento com o estado abstrato. Esse processo recebe o nome de relação de refinamento, sendo concretizado através de invariantes de ligação (gluing invariants). Nessa relação, cada transição possível de ser feita no estado concreto deve corresponder a uma transição permitida para o modelo abstrato. MACHINE MA VARIABLES vA INVARIANT InvA (vA ) INITIALISATION InitA (vA ) OPERATIONS rA ←− OPA (pA ) = b PRE PreA (vA , pA ) THEN SA (vA , pA , rA ) END END REFINEMENT MR REFINES MA VARIABLES vR INVARIANT InvR (vA , vR ) INITIALISATION InitR (vR ) OPERATIONS rR ←− OPR (pR ) = b PRE PreR (vR , pR ) THEN SR (vR , pR , rR ) END END Figura 3.2: Esquema genérico de um refinamento. 49 Salienta-se que as operações do refinamento estão sujeitas a regras restritivas que devem ser obedecidas para possibilitar o refinamento, quais sejam, (i) o refinamento deve preservar o mesmo número de operações do módulo refinado e (ii) a assinatura da operação abstrata não pode ser modificada no refinamento. A restrição (ii) significa que o nome da operação e a ordem, nome e tipo de seus parâmetros de entrada e valores de retorno não podem ser modificados. Essas restrições podem trazer certas dificuldades ao desenvolvimento de software. No capítulo 4, discute-se duas soluções para contornar a restrição de mudança de interface no refinamento, o que beneficia diretamente o refinamento no método BSmart. 3.2.1 Obrigações de prova As obrigações de prova do refinamento possuem maior complexidade quando comparadas àquelas para uma máquina B simples. Em um refinamento deve ser verificada a preservação do estado concreto (semelhante a uma máquina) e, adicionalmente, deve-se garantir a correção do refinamento de dados e de operações. As obrigações de prova do refinamento, que relacionam estados abstratos e concretos, são apresentadas e discutidas a seguir, baseando-se na abordagem do livro de B de Schneider [Sch01]. Uma discussão aprofundada sobre elas pode ser encontrada no B-Book [Abr96]. A fórmula abaixo apresenta a obrigação de prova do refinamento relacionada à inicialização abstrata. O invariante de ligação (InvR ) estabelece que cada transição de estado que é feita pela inicialização concreta tem uma inicialização correspondente no modelo abstrato. [InitR (vR )](¬[InitA (vA )](¬InvR (vA , vR ))) Observa-se, na obrigação de prova, as negações em (¬[InitA (vA )](¬InvR (vA , vR ))). Elas atestam a possibilidade do invariante de ligação ser estabelecido apenas por “algumas” transições de estado abstratas. Ou seja, cada estado concreto deve estar relacionado a um estado abstrato, no entanto, o contrário não precisa, necessariamente, ocorrer. Ressalta-se que, para o caso das operações, como demonstrado no lado esquerdo da obrigação de prova, deve-se levar em consideração a validade do estado previamente modificado, assim como o estabelecimento da pré-condição. Uma vez que essas premissas forem satisfeitas, deve-se verificar se todos os estados que podem ser alcançados pelo refinamento podem também ser atingidos pela aplicação da operação abstrata, de acordo com o que foi especificado no invariante de ligação. De outro modo, utilizando-se o conceito de simulação, pode-se dizer que o comportamento observado quando a operação concreta é aplicada poderá ser simulado pela 50 aplicação correspondente da operação abstrata, para os mesmos valores de entrada, sem que haja diferença para o observador. InvA (vA ) ∧ InvR (vA , vR ) ∧ PreA (vA , pA ) ⇒ PreR (vR , pR ) ∧ [SR (vR , pR , rR )](¬[SA (vA , pA , rA )](¬InvR (vA , vR ))) No que tange às pré-condições abstratas e concretas, vale salientar que a primeira não se faz necessária na operação concreta, desde que ela seja suficiente para estabelecer as condições para a correta aplicabilidade de ambas as operações. Entretanto, em alguns casos, pode ser útil complementar a operação concreta com alguma informação de pré-condição. Nesses casos, a operação concreta não deve contradizer o que foi estabelecido na operação abstrata, isto é, ela deve ser válida sempre que a operação abstrata for válida. Em outras palavras, a pré-condição concreta deve ser sempre mais fraca que a sua correspondente abstrata, tendo em vista evitar um comportamento concreto mais restritivo que o abstrato. 3.2.2 Implementação Conforme apreciado nas seções anteriores, no desenvolvimento em B, inicialmente é possível utilizar-se de substituições não-determinísticas, úteis para especificações funcionais abstratas. À medida em que o desenvolvimento avança, através dos refinamentos, substituições concretas são oferecidas ao especificador, de modo que a abstração no desenvolvimento é progressivamente diminuída a cada passo de refinamento. O último estágio possível de refinamento é a implementação. Com base nesse módulo, é possível a tradução do desenvolvimento no código de linguagem de programação imperativa, tal qual C, Java ou ADA. A figura 3.3 exibe um protótipo de uma máquina de implementação e suas principais cláusulas. No intuito de permitir a tradução de B para uma linguagem de programação, a implementação é um refinamento que possui uma notação mais restritiva. O desenvolvedor, no módulo de implementação, pode utilizar-se apenas de dados concretos e substituições determinísticas, tais como, sequência, condicional, repetição (while), etc. Além disso, é interessante citar outras características importantes de uma implementação, a saber: • A implementação não possui a cláusula VARIABLES (ou ABSTRACT_VARIABLES). O estado concreto deve ser encapsulado em variáveis de máquinas externas, incluídas como bibliotecas utilizando-se a cláusula IMPORTS. Tais variáveis são então relacionadas com as variáveis abstratas através de um invariante de ligação. • Uma variável só pode ser modificada através das operações da máquina importada que a 51 IMPLEMENTATION MI REFINES MR IMPORTS MIMP CONSTANTS c PROPERTIES Prop(c) VALUES Val(c) CONCRETE_VARIABLES vC INVARIANT Inv(vC , vR , vIMP ) INITIALISATION Init(vC , vIMP ) OPERATIONS r ←− op(p) = b BEGIN S(vIMP , p, r) END END Figura 3.3: Um protótipo do módulo de implementação. define. • As constantes e conjuntos adiados que foram declarados em módulos abstratos (refinamentos e/ou máquina) de uma implementação devem receber um valor concreto na cláusula VALUES da implementação. Destaca-se que a notação de implementação permitida para uma linguagem de programação alvo é conhecida como B0. Tendo em vista minimizar o conjunto de construções que o tradutor é capaz de lidar, a notação B0 impõe regras mais restritivas com respeito à atribuição de tipos, valoração de constantes e conjuntos, a forma sintática permitida para expressões, dentre outras. Portanto, as restrições do B0 são dependentes da linguagem alvo da tradução e devem ser definidas pelo desenvolvedor do tradutor. 3.3 O Formalismo Event-B A presente seção tem por objetivo apresentar o formalismo Event-B e contextualizar os motivos pelos quais neste trabalho permanecemos com o uso do B clássico. Conforme apresentado neste capítulo, o método formal B é voltado à especificação, desenvolvimento e verificação de programas sequenciais como base em refinamentos. Observa-se que o método formal EventB [Abr10] é mais recente e recebe influência de B. No entanto, essa nova abordagem não deve ser encarada como uma evolução do método formal B, como poderia-se supor, uma vez que o foco de Event-B é diferente do B clássico, a saber, a modelagem e a verificação de sistemas re- 52 ativos concorrentes. Dessa forma, o comportamento de uma máquina Event-B, sua notação e as obrigações de prova para verificação de máquinas e refinamentos são voltadas à especificação dessa categoria de sistemas, diferenciando-se do B tradicional. O modelo de um sistema em Event-B é composto por máquinas, contextos e refinamentos. Nos contextos declaram-se os componentes estáticos de uma máquina, formado por conjuntos, constantes e as propriedades que esses elementos devem obedecer. A parte dinâmica é a máquina em si, cujos principais elementos são as variáveis de estado, o invariante e os eventos. Observa-se que os eventos, assim como as operações do modelo em B clássico, relacionamse à mudança de estado da especificação. A aplicação de um evento é condicionada à satisfação da sua guarda, composta por uma conjunção de predicados. Um evento pode ser aplicado em um determinado estado, sempre que a sua guarda seja avaliada como verdadeira. No entanto, é importante enfatizar que a aplicação de um evento é atômica e, mesmo que em algum estado mais de um evento possa ser aplicado (suas guardas são simultaneamente satisfeitas), ainda assim apenas um deles, de forma não determinística, será aplicado. MACHINE Transport CONTEXT Transport_Context SEES Transport_Context SETS VARIABLES balance card_type CARDS CONSTANTS gratuitous INVARIANTS student inv1 : balance ∈ N inv2 : balance ≥ 0 inv3 : card_type ∈ CARDS inv4 : card_type = gratuitous ⇒ balance = 0 entire AXIOMS axm1 : partition(CARDS, {gratuitous}, {student}, {entire}) END Figura 3.4: Máquina Event-B Transport (esq.) e contexto Transport_Constants (dir.) Como exemplo, o modelo Event-B da aplicação Java Card de bilhetagem eletrônica Transport do capítulo 2 é exibido nas figuras 3.4 e 3.5. A figura 3.4 apresenta a declaração das variáveis de estado e a especificação do invariante, no qual são inseridas as restrições de que o cartão não deve ter saldo negativo (inv2) e, caso o cartão seja de gratuidade, o saldo deve ser 0 (inv4). Pode-se observar que cada restrição é inserida em uma linha separada, precedida por uma marcação (inv1 .. inv4). Essas marcações são exigência da ferramenta Rodin [ABH+ 10], utilizada na construção deste exemplo, para que ela possa gerenciar cada elemento da especificação, facilitando a localização de erros e a construção e exibição para o usuário de obrigações de prova. Ainda na figura 3.4, tem-se o módulo de contexto Transport_Constants, que declara o conjunto CARDS, utilizado na máquina Transport. Os possíveis elementos do conjunto (especifi- 53 cado na cláusula AXIOMS) são as contantes gratuitous, entire e student, que correspondem aos tipos de cartão que podem ser emitidos. EVENTS Initialisation begin act1 : balance := 0 act2 : card_type := entire end Event addCredit = b any cr where grd1 : grd2 : grd3 : grd4 : grd5 : cr ∈ N cr > 0 card_type 6= gratuitous balance + cr ≥ 0 balance + cr ∈ N then act1 : balance := balance + cr end END Figura 3.5: Eventos da especificação Transport Observa-se pela figura 3.5 que na máquina Transport são definidos dois eventos, a saber, o evento de inicialização e o evento addCredit que modela a adição de créditos em um cartão. Ressalta-se que o evento de inicialização ocorre antes da ativação de qualquer outro evento para estabelecer o estado inicial da máquina de forma consistente com o seu invariante. Um evento pode ser especificado através de três formas [ABH+ 10], são elas: 1. evt = b any t where P(t, v) then S(t, v) end 2. evt = b where P(v) then S(v) end 3. evt = b begin S(v) end A primeira forma é a mais geral e define um evento que possui parâmetros de entrada (t), uma guarda que atua sobre os parâmetros e as variáveis de estado (P(t, v)) e uma ação (S(t, v)). A ação é o componente responsável pela mudança de estado do evento caso a sua guarda seja estabelecida. As outras formas especificam, respectivamente, um evento sem parâmetros e um evento apenas com ação. Neste último caso, considera-se que a guarda do evento é sempre verdadeira. É importante observar que as ações em Event-B são restritas a um pequeno conjunto de ações simples para atualização das variáveis de estado (também presentes em B na forma de 54 substituições), conforme pode ser visto na figura 3.6. Essa característica, para o tipo de desenvolvimento requerido pelas aplicações smart card caracterizaria uma certa dificuldade na especificação de um serviço. Por exemplo, caso necessitássemos especificar um serviço que pode tomar mais de uma ação a partir da avaliação de condições, o que é comum em qualquer aplicação, então teríamos que especificar cada ação em um evento distinto, com a sua condição de aplicação definida como guarda. Atribuição simples (becomes equal to): x := E (x é substituído pela expressão E) Escolha a partir de um conjunto (becomes in): x :∈ S (x recebe um elemento qualquer do conjunto S) Escolha a partir de um predicado (becomes such that): x :| P (x recebe um valor qualquer que satisfaça ao predicado P) Figura 3.6: Ações presentes em Event-B. Ressalta-se que Event-B é mais flexível que B no que diz respeito a mecanismos permitidos para especificação e refinamento. Por exemplo, uma máquina pode ser decomposta em mais de uma máquina para gerenciar a complexidade da especificação. Um evento, por sua vez, pode ser refinado por mais de um evento. Finalmente, mais de um evento pode ser unido em um evento único. É também possível introduzir novos eventos em um refinamento. Nesse caso, o evento concreto implicitamente refina um evento abstrato contendo uma substituição skip em seu corpo e o especificador deve assegurar o novo evento não leve a máquina a um estado de deadlock. Evidencia-se que na abordagem utilizada no método BSmart, as aplicações smart card são traduzidas após serem especificadas, refinadas e implementadas em B. A composição de máquinas e refinamentos em B, conforme demonstrado neste capítulo, é mais restritiva que Event-B. Por outro lado, B oferece um ambiente mais adequado para se introduzir um estilo de desenvolvimento algorítmico, composto por diversas substituições, muitas delas possuindo uma relação estrita com construções encontradas em linguagens, tais como C e Java. Além disso, ressaltase que o refinamento de implementação não está presente em Event-B. Como consequência, as substituições determinísticas necessárias para a geração de código não são parte da notação Event-B, tais como, condicional (IF), sequência (“;”), repetição (WHILE), etc. Diante do exposto nesta seção, conclui-se que a especificação e refinamento de uma aplicação Java Card, seguindo o método BSmart em Event-B traria um série de dificuldades, devido ao paradigma de especificação em Event-B não ser adequado à especificação de sistemas sequenciais. As etapas de tradução também seriam prejudicadas, uma vez que não há em Event-B um módulo de implementação, determinístico, a partir do qual as aplicações Java Card fossem 55 traduzidas. No entanto, uma oportunidade para a aplicação de Event-B seria na especificação de propriedades globais do sistema smart card, por exemplo, no modelo da comunicação entre as aplicações cliente e cartão. Essa possibilidade será explorada como trabalho futuro. 56 4 O Método BSmart Conforme discutido nos capítulos anteriores, em virtude de estarem geralmente relacionadas a dados financeiros ou particulares do seu portador, o acesso às informações armazenadas em um smart card deve ser protegido e o seu estado deve permanecer consistente entre as diversas sessões de comunicação com a aplicação cliente. Nesse sentido, o ambiente de execução Java Card fornece mecanismos para permitir a execução segura dessas aplicações, tais como o controle de transação e o applet firewall. Observa-se que, apesar do sistema Java Card fornecer ao desenvolvedor instrumentos que visam preservar informações e melhorar a segurança do ambiente smart card, erros podem ser facilmente introduzidos em todas as fases do desenvolvimento de software. Isso normalmente ocorre devido ao entendimento incorreto dos requisitos especificados, fazendo com que os erros sejam propagados para as fases posteriores. Sendo um domínio de aplicação crítico, falhas em uma aplicação smart card podem ter consequências inaceitáveis, tais como perdas financeiras ou o acesso indevido a informações. Logo, o emprego de técnicas e ferramentas de apoio à engenharia de software rigorosa durante o processo de especificação e desenvolvimento, tal qual o método proposto nesta tese, é um meio de melhorar a confiabilidade na aplicação implementada. É interessante ressaltar que as características particulares do ambiente smart card fornecem um domínio ideal para a introdução de técnicas formais, como o método B. Essas aplicações, devido às restrições do hardware smart card, normalmente são pequenas, tanto em relação ao número de linhas de código-fonte, quanto ao tamanho do bytecode Java gerado. Além disso, a maior parcela do desenvolvimento smart card está relacionada à implementação de uma API contendo os serviços da aplicação cartão. Neste capítulo apresenta-se o método BSmart, uma especialização do método formal B que tem por objetivo proporcionar o desenvolvimento rigoroso de aplicações smart cards na linguagem Java Card. O processo de desenvolvimento com o método BSmart descreve como a aplicação Java Card pode ser gerada, iniciando pela sua especificação abstrata e seguindo com o desenvolvimento de modelos de refinamentos em B tendo em vista introduzir, gradativamente, 57 os aspectos relacionados a Java Card. O uso do método B em todo o desenvolvimento, com o suporte de uma ferramenta de verificação que prove todas as obrigações prova requeridas, tal qual o Atelier B [Cle12a], garante a preservação das propriedades funcionais da aplicação, desde a sua especificação abstrata até o módulo de implementação que dará origem à aplicação Java Card. O presente capítulo é dividido conforme descrito a seguir. A seção 4.1 fornece uma visão geral do método BSmart. A especificação abstrata que inicia o desenvolvimento é descrita na seção 4.2. Em seguida, a seção 4.3 enfatiza o desenvolvimento da aplicação para o cartão e a sua formalização em B. A seção 4.4 trata da verificação da mudança de interface entre o componente abstrato inicial (Java) e o seu correspondente concreto (Java Card). A geração da API de suporte à comunicação da aplicação cliente com o cartão é tratada na seção 4.5 e o estado atual do suporte de ferramentas ao método é descrito na seção 4.6. Por sua vez, a seção 4.7 demonstra a aplicabilidade do retrenchment para a verificação apresentada na seção 4.4. Por fim, a seção 4.8 avalia o método e enfatiza os desenvolvimentos futuros. 4.1 Processo de Desenvolvimento O desenvolvimento com base no método BSmart tem início com a especificação formal abstrata do software smart card como uma máquina B. A partir dessa máquina, seguem-se duas linhas de desenvolvimento (figura 4.1), uma delas corresponde ao applet que será instalado no cartão, e a outra está relacionada à API de suporte à aplicação host. Ressalta-se que a ordem do desenvolvimento, após a especificação inicial, é irrelevante, uma vez que a geração da API para a aplicação cliente é independente do desenvolvimento do applet. Figura 4.1: Desenvolvimento com o método BSmart. 58 applet O desenvolvimento é um processo de refinamento da especificação inicial. Os aspectos relacionados à aplicação Java Card, e à sua comunicação com a aplicação cliente, são progressivamente adicionados em cada refinamento. Ao final da cadeia de refinamentos, ao se atingir a implementação B, é possível traduzir a especificação concreta, gerando o applet Java Card. aplicação host Com respeito à tradução para o cliente, é importante observar que não é a aplicação host como um todo que está sendo gerada, tendo em vista ser impossível lidar com a lógica de negócio que varia de uma aplicação para outra. O que se fornece é uma API, gerada a partir da especificação inicial com o auxílio de ferramenta, que encapsula os aspectos de baixo nível da comunicação entre as aplicações do sistema Java Card. Assim, ao usar a API, a aplicação host pode comunicar-se com o cartão em um nível mais elevado, como se estivesse lidando com uma aplicação Java desktop comum, abstraindo totalmente os detalhes dos protocolos do sistema smart card. O suporte automatizado ao desenvolvedor que segue o método é fornecido por algumas ferramentas, descritas em maiores detalhes na seção 4.6, que recebem como entrada um módulo B e geram código para B ou Java/Java Card. Nesse caso, é possível gerar o refinamento full function (seção 4.3.1), o código Java Card da aplicação cartão a partir da sua implementação B, assim como as classes de suporte à comunicação da aplicação cliente (host) com os serviços no cartão. Por outro lado, observa-se que os demais módulos requeridos devem ser criados de forma manual, por exemplo, a especificação inicial e as implementações B. Destaca-se uma importante questão que emergiu durante o desenvolvimento do método, que concerne ao tratamento da incompatibilidade da interface entre o modelo abstrato inicial e o modelo concreto. Em um refinamento em B não é possível a mudança na assinatura (interface) entre uma operação concreta e a sua correspondente abstrata, mesmo quando há, de forma implícita, a compatibilidade entre os estados que podem ser alcançados nas especificações abstrata e concreta. Assim, caso verifique-se necessária a mudança de interface, por exemplo, para modificar a representação de um tipo abstrato utilizando-se tipos compatíveis com Java Card, ou para representar a recepção e o retorno de informações através do buffer apdu, é preciso utilizar um processo de verificação alternativo (demonstrado na seção 4.4), mas equivalente, em capacidade de verificação, ao refinamento tradicional. Nas próximas seções, serão detalhados os aspectos relacionados às duas linhas de desenvolvimento com o método BSmart. A formalização do refinamento do lado cartão é apresentada na seção 4.3, enquanto que os componentes da API para o cliente são descritos na seção 4.5. 59 4.2 Especificação Abstrata Conforme explanado na introdução deste capítulo, o desenvolvimento formal do applet Java Card inicia com a sua especificação em alto nível, desenvolvida pelo usuário do método como uma máquina B. Por se tratar da primeira descrição funcional dos serviços, não há necessidade de se introduzir aspectos relacionados a Java Card, pois eles serão progressivamente adicionados nos refinamentos concretos posteriores. Para ilustrar o processo de especificação e refinamento da aplicação Java Card seguindo o método BSmart, introduzir-se-á um modelo genérico (template) desse processo, desenvolvido utilizando máquinas e refinamentos B. O propósito dessa abordagem é formalizar e documentar todas as etapas do desenvolvimento sem atrelá-lo a um estudo de caso ou toy example em particular. Observa-se que as obrigações de prova dos módulos apresentados neste capítulo foram corretamente verificadas através do provador da ferramenta Atelier B 4.01. Ressalta-se que, na versão atual, não é possível que os modelos descritos sejam instanciados de forma automática para gerar uma aplicação em particular. Ou seja, cada nova aplicação deve seguir o desenvolvimento proposto, porém deverá verificar novamente, através de ferramenta, as obrigações de prova requeridas em cada etapa. Inicialmente, é importante apresentar o módulo J_Context (figura 4.2), que inclui as definições abstratas para cada elemento significativo da máquina que representa a especificação inicial do desenvolvimento. O conjunto J_TYPE representa um sistema de tipos compatível com Java. Nesse sistema é possível ter, por exemplo, o tipo inteiro (int) ou qualquer conjunto representando um tipo abstrato de dado. Na cláusula PROPERTIES, funções definem os elementos relacionados à mudança de estado e à verificação da máquina. Na ordem em que aparecem na figura 4.2, tem-se o invariante (j_inv), as pré-condições das operações (j_pre), a atualização do estado (j_stf ) e o retorno de dados em uma operação (j_ouf ), bem como a pós-condição após esse retorno (j_pos). Ainda na cláusula na cláusula PROPERTIES de J_Context, algumas propriedades adicionais (predicados) sobre os componentes da máquina são declaradas, tendo em vista assegurar a correta verificação do componente abstrato. As duas primeiras visam assegurar que as transições de estado (j_stf ) e as saída das operações (j_ouf ) sejam realizadas sob um estado (j_inv(v1) = TRUE) e parâmetros de entrada (j_pre(v2) = TRUE) válidos. Uma máquina cuja verificação seja correta, também deve satisfazer as outras duas propriedades. A penúltima, sobre j_stf, visa garantir que a transição de estados em uma operação seja válida de acordo com as restrições impostas pelo invariante. A última, sobre j_ouf, estabelece que o estado posterior ao retorno de 60 MACHINE J_Context SETS J_TYPE CONCRETE_CONSTANTS j_stf , j_ouf , j_inv , j_pre , j_pos PROPERTIES j_inv ∈ J_TYPE → BOOL ∧ j_pre ∈ J_TYPE → BOOL ∧ j_stf : ( J_TYPE × J_TYPE ) → 7 J_TYPE ∧ j_ouf : ( J_TYPE × J_TYPE ) → 7 J_TYPE ∧ j_pos ∈ J_TYPE → BOOL ∧ ∀ ( v1 , v2 ) . ( v1 ∈ J_TYPE ∧ v2 ∈ J_TYPE ∧ j_inv ( v1 ) = TRUE ∧ j_pre ( v2 ) = TRUE =⇒ v1 , v2 ∈ dom ( j_stf ) ) ∧ ∀ ( v1 , v2 ) . ( v1 ∈ J_TYPE ∧ v2 ∈ J_TYPE ∧ j_inv ( v1 ) = TRUE ∧ j_pre ( v2 ) = TRUE =⇒ v1 , v2 ∈ dom ( j_ouf ) ) ∧ ∀ ( v1 , v2 ) . ( v1 ∈ J_TYPE ∧ v2 ∈ J_TYPE ∧ v1 , v2 ∈ dom ( j_stf ) =⇒ j_inv ( j_stf ( v1 , v2 ) ) = TRUE ) ∧ ∀ ( v1 , v2 ) . ( v1 ∈ J_TYPE ∧ v2 ∈ J_TYPE ∧ v1 , v2 ∈ dom ( j_ouf ) =⇒ j_pos ( j_ouf ( v1 , v2 ) ) = TRUE ) Figura 4.2: Definição do tipo Java e das propriedades para a formalização dos componentes da especificação abstrata. dados ao cliente (pós-condição) deve preservar a correção do invariante. A especificação abstrata que inicia o desenvolvimento é representada pela máquina J_App (figura 4.3). Os identificadores da máquina foram prefixados com “J_” para representar que o seu estado utiliza-se de dados abstratos compatíveis com Java. Por sua vez, os componentes Java Card concretos serão prefixados com “JC_”. MACHINE J_App SEES J_Context ABSTRACT_VARIABLES j_v INVARIANT j_v ∈ J_TYPE ∧ j_inv ( j_v ) = TRUE INITIALISATION ANY val WHERE val ∈ J_TYPE ∧ j_inv ( val ) = TRUE THEN j_v := val END OPERATIONS j_r ← j_operation ( j_param ) = PRE j_param ∈ J_TYPE ∧ j_pre ( j_param ) = TRUE THEN j_v := j_stf ( j_v , j_param ) k j_r := j_ouf ( j_v , j_param ) END Figura 4.3: Representação da especificação abstrata que inicia o desenvolvimento BSmart. A máquina J_App inclui J_Context (SEES J_Context), dessa forma torna-se possível acessar as propriedades definidas nesse módulo. O estado global da máquina J_App é representado pela variável j_v, declarada no invariante como sendo do tipo Java (J_TYPE). Do mesmo modo, recebem o tipo J_TYPE os parâmetros recebidos (j_param) e retornados (j_r) pela operação (j_operation), indicando que a máquina abstrata possui um sistema de tipos compatível com Java. 61 No corpo da operação j_operation, a modificação do estado (j_v) é representada pela substituição da definição j_stf ( j_v, j_param), que recebe o valor do parâmetro (j_param) e da variável de estado (j_v) e retorna um valor J_TYPE. Por sua vez, o modelo do retorno da operação é feito através da definição j_ouf ( j_v , j_param), que irá atualizar a saída (j_r). Nas seções seguintes, serão apresentados os módulos obrigatórios ao desenvolvimento da aplicação cartão seguindo o método BSmart. Posteriormente, na seção 4.5, detalham-se os componentes da API de suporte à aplicação host. 4.3 Desenvolvimento da Aplicação Java Card A figura 4.4 fornece uma visão geral do desenvolvimento com o método BSmart para a aplicação cartão e dos artefatos que são criados no decorrer desse processo. Após a especificação inicial (J_App), ou de um refinamento posterior dela, deve-se desenvolver uma especificação equivalente, mas que possua definições de tipos de dados compatíveis com Java Card. De posse desse modelo (JC_App), é possível proceder com o seu refinamento em direção à aplicação cartão. Figura 4.4: Uma visão dos artefatos que compõem o desenvolvimento da aplicação Java Card do lado cartão (não inclui aplicação cliente). Ressalta-se que é possível refinar o componente JC_App em diversos níveis, com o objetivo de gerenciar a complexidade ou reduzir o esforço de verificação do desenvolvimento B. No entanto, ao menos um refinamento torna-se obrigatório (JC_App_FF_r, na figura 4.4), tendo por 62 objetivo possibilitar a especificação de condições de erro que são previamente conhecidas. Essas situações de erro são retiradas das pré-condições das operações e inseridas em substituições condicionais em seu corpo, em um estilo de especificação denominado de full function. A aplicação final do lado cartão resulta da tradução de duas implementações principais, a saber, JC_App_i, refinamento dos serviços que a aplicação irá oferecer, e JC_Applet_i, refinamento da especificação Applet que modela a classe homônima da API Java Card. Como pode ser vislumbrado na figura 4.4, esses desenvolvimentos seguem duas linhas distintas, mas não independentes, sendo JC_Applet_i a porta de entrada para os serviços oferecidos por JC_App_i. Para tanto, JC_Applet_i importa o modelo abstrato de JC_App_i (JC_App), o que torna possível referenciar seus serviços, por exemplo, na implementação do método process, ou em algum procedimento de inicialização no método install. Outros módulos dos quais as implementações anteriores dependem também podem ser implementados, tais como as máquinas de contexto. MACHINE JC_App SEES JC_Context ABSTRACT_VARIABLES jc_v INVARIANT jc_v ∈ JC_TYPE ∧ jc_inv ( jc_v ) = TRUE INITIALISATION ANY val WHERE val ∈ JC_TYPE ∧ jc_inv ( val ) = TRUE THEN jc_v := val END OPERATIONS jc_r ← jc_operation ( jc_param ) = PRE jc_param ∈ JC_TYPE ∧ jc_pre ( jc_param ) = TRUE THEN jc_v := jc_stf ( jc_v , jc_param ) k jc_r := jc_ouf ( jc_v , jc_param ) END END Figura 4.5: Versão “Java Card” da máquina J_App. Utilizar-se-á a representação do modelo Java Card (JC_App), conforme apresentado na figura 4.5, como referência para demonstrar o desenvolvimento e a verificação da proposta até o módulo concreto de implementação da aplicação Java Card. Percebe-se que o módulo Java Card desenvolvido é similar àquele que representa a especificação inicial (J_App), exceto pelo fato de que todos os componentes de estado são agora restritos para um sistema de tipos compatível com Java Card, representado pela constante JC_TYPE, definida na máquina JC_Context (fig. 4.6). Observa-se que o componente de contexto também contém as definições estáticas para os componentes significativos da especificação concreta, assim como foi feito para o modelo Java, em J_Context (fig. 4.2). As propriedades que asseguram a consistência da máquina concreta também foram especificadas. Adicionalmente, na cláusula ASSERTIONS, foram inseridas mais 63 MACHINE JC_Context SETS JC_TYPE CONCRETE_CONSTANTS jc_c , jc_stf , jc_ouf , jc_inv , jc_pre , jc_pos PROPERTIES jc_c ∈ JC_TYPE ∧ jc_inv ∈ JC_TYPE → BOOL ∧ jc_pre ∈ JC_TYPE → BOOL ∧ jc_stf : ( JC_TYPE × JC_TYPE ) → 7 JC_TYPE ∧ jc_ouf : ( JC_TYPE × JC_TYPE ) → 7 JC_TYPE ∧ jc_pos ∈ JC_TYPE → BOOL ∧ ∀ ( v1 , v2 ) . ( v1 ∈ JC_TYPE ∧ v2 ∈ JC_TYPE ∧ jc_inv ( v1 ) = TRUE ∧ jc_pre ( v2 ) = TRUE =⇒ v1 , v2 ∈ dom ( jc_stf ) ) ∧ ∀ ( v1 , v2 ) . ( v1 ∈ JC_TYPE ∧ v2 ∈ JC_TYPE ∧ jc_inv ( v1 ) = TRUE ∧ jc_pre ( v2 ) = TRUE =⇒ v1 , v2 ∈ dom ( jc_ouf ) ) ∧ ∀ ( v1 , v2 ) . ( v1 ∈ JC_TYPE ∧ v2 ∈ JC_TYPE ∧ v1 , v2 ∈ dom ( jc_stf ) =⇒ jc_inv ( jc_stf ( v1 , v2 ) ) = TRUE ) ∧ ∀ ( v1 , v2 ) . ( v1 ∈ JC_TYPE ∧ v2 ∈ JC_TYPE ∧ v1 , v2 ∈ dom ( jc_ouf ) =⇒ jc_pos ( jc_ouf ( v1 , v2 ) ) = TRUE ) ASSERTIONS ∀ ( v1 , v2 ) . ( v1 ∈ JC_TYPE ∧ v2 ∈ JC_TYPE ∧ jc_inv ( v1 ) = TRUE ∧ jc_pre ( v2 ) = TRUE =⇒ jc_inv ( jc_stf ( v1 , v2 ) ) = TRUE ) ∧ ∀ ( v1 , v2 ) . ( v1 ∈ JC_TYPE ∧ v2 ∈ JC_TYPE ∧ jc_inv ( v1 ) = TRUE ∧ jc_pre ( v2 ) = TRUE =⇒ jc_pos ( jc_ouf ( v1 , v2 ) ) = TRUE ) END Figura 4.6: Definição do tipo Java Card e das propriedades para a formalização dos componentes da especificação concreta. duas propriedades, com o objetivo de auxiliar a ferramenta de verificação nas provas do módulo. Elas visam atestar que se o estado (jc_inv (v1) = TRUE) e os parâmetros de uma operação (jc_pre (v2) = TRUE) forem válidos antes da sua aplicação, então a aplicação da operação (jc_inv (jc_stf (v1, v2)) = TRUE)) e os valores retornados (jc_pos (jc_ouf (v1, v2)) = TRUE)) por ela, devem levar a máquina para um estado posterior no qual o invariante continue verdadeiro. Embora apresentem interfaces distintas, cumpre enfatizar que esses modelos são equivalentes, ou seja, são capazes de efetuar as mesmas transições entre estados através da aplicação de suas operações. Conforme discutido na introdução deste capítulo, tendo em vista que a interface das operações do desenvolvimento abstrato (Java) difere das suas correspondentes concretas (Java Card), a verificação da equivalência entre esses modelos não pode ser demonstrada pelo refinamento direto em B. A seção 4.4 apresenta uma abordagem de verificação, desenvolvida nesta tese, que contorna essa restrição permanecendo no rigor do refinamento em B. 4.3.1 Geração do refinamento Full Function Seguindo-se com a apresentação do desenvolvimento formal do método, descreve-se, nesta seção, um refinamento adicional que visa tornar o desenvolvimento mais próximo de uma aplicação Java Card típica. Essa etapa de refinamento é essencial à implementação do applet Java 64 Card. É interessante observar que, em uma máquina B, as restrições a uma operação são geralmente especificadas em sua pré-condição, devendo essas serem respeitadas antes da aplicação da operação. Todavia, para que seja possível a tradução dessas pré-condições na forma de condições de erro que, caso ocorram, devem ser reportados ao cliente, faz-se necessário oferecer operações totais (full function). O refinamento ora apresentado (figura 4.7) aplica o modelo de operação full function, no qual as pré-condições são enfraquecidas, de forma que a pré-condição define apenas o tipo dos parâmetros recebidos. As demais restrições são então tratadas no corpo da operação, através de substituições condicionais, cuja não-satisfação da condição leva ao lançamento de uma exceção. A máquina ISOException, parte da biblioteca KitSmart (capítulo 6), representa um modelo simples da classe de exceção de mesmo nome presente na API Java Card. Se o refinamento full function não fosse aplicado, não seria possível a geração de código para a verificação dessas condições de erro no código Java Card gerado. REFINEMENT JC_App_FF_r REFINES JC_App SEES JC_Context, JC_App_FF_Context INCLUDES ISOException.ISOException VARIABLES jc_vr INVARIANT jc_vr ∈ JC_TYPE ∧ jc_v = jc_vr INITIALISATION ... OPERATIONS jc_r ← jc_operation ( jc_param ) = PRE jc_param ∈ JC_TYPE THEN IF ¬ ( jc_pre (jc_param) ) = TRUE THEN ISOException.throwIt(exception_id_1) ELSE jc_vr := jc_stf ( jc_vr , jc_param ) k jc_r := jc_ouf ( jc_vr , jc_param ) END END END MACHINE JC_App_FF_Context SEES JC_Context CONSTANTS exception_id_1, exception_id_2, exception_id_n, jc_operation_1_INS, jc_operation_2_INS, jc_operation_n_INS PROPERTIES exception_id_1 ∈ JC_TYPE ∧ exception_id_2 ∈ JC_TYPE ∧ exception_id_n ∈ JC_TYPE ∧ jc_operation_1_INS ∈ JC_TYPE ∧ jc_operation_2_INS ∈ JC_TYPE ∧ jc_operation_n_INS ∈ JC_TYPE END Figura 4.7: Versão full function de JC_App (esq.) e máquina de contexto (dir.). Ressalta-se que nesta etapa também é definida uma máquina de contexto (lado direito da figura 4.7), responsável por incluir informações estáticas úteis ao desenvolvimento. Ela contém o identificador de cada condição de exceção (exception_id_x) e códigos de instrução (jc_operation_x_INS) para identificar unicamente cada serviço. Quaisquer outras constantes úteis à aplicação Java Card também podem ser inseridas neste módulo de contexto. 65 4.3.2 Implementação em B do desenvolvimento Java Card dos Serviços O último estágio de refinamento consiste na implementação B0 dos módulos da aplicação Java Card que será inserida no cartão. No método BSmart essa atividade é dividida na implementação do desenvolvimento dos serviços, tratado nesta seção, e na implementação do applet, discutida na seção 4.3.3. A partir desses modelos concretos é possível traduzir a aplicação Java Card através da ferramenta geradora de código. O módulo de implementação principal JC_App_i (figura 4.8) especifica os serviços oferecidos ao componente cliente. Usualmente, ele referencia (importa) outras especificações, tais como máquinas de biblioteca e outros módulos necessários à implementação dos serviços. Ressalta-se que o estado da implementação deve estar encapsulado em outras máquinas, que também devem ser importadas. Na figura 4.8, isso é representado pela máquina (jc_cvar.JCType_lib), responsável por encapsular a variável de estado jc_var. Nesse caso, deve-se relacionar, no invariante da implementação, a variável jc_var à variável jc_type_var da máquina jc_cvar.JCType_lib. A partir desse momento, toda modificação desse componente do estado deve ser feita sobre jc_type_var, através das operações de jc_cvar.JCType_lib. IMPLEMENTATION JC_App_i REFINES JC_App_FF_ref SEES JC_Context, JC_AppFF_Context, InterfaceContext IMPORTS ISOException.ISOException, apdu.Apdu, jc_cvar.JCType_lib INVARIANT jc_val = jc_cvar.jc_type_var INITIALISATION jc_cvar.init OPERATIONS jc_r ← jc_operation ( jc_param ) = VAR result IN IF ¬ (jc_pre (jc_param) = TRUE) THEN ISOException.throwIt(exception_id) ELSE result ← jc_cvar.concrete_operation(jc_param); END END END Figura 4.8: Implementação B da aplicação Java Card. Observa-se que o prefixo jc_cvar define uma instância do componente JCType_lib. Caso seja necessário incluir outras instâncias dessa máquina, o usuário deve atribuir um novo prefixo para cada instância. O prefixo deve ser utilizado para referenciar variáveis e operações da máquina, como no caso da operação init, responsável por inicializar a variável de estado jc_var (INITIALISATION jc_cvar.init). Na implementação da aplicação Java Card, recomenda-se que as máquinas da biblioteca 66 KitSmart (capítulo 6) sejam importadas com o propósito de auxiliar o processo de geração de código e de verificação. Elas representam classes que geralmente são utilizadas em uma aplicação Java Card típica. A aplicação de uma operação de uma máquina da API, como por exemplo, a operação throwIt da máquina ISOException, referenciada em JC_App_i, é diretamente traduzida na chamada do método ISOException.throwIt correspondente de Java Card. O uso desses modelos também permite verificar a ordem adequada da chamada de operações e as pré-condições para a sua correta aplicação. Destaca-se que no corpo da operação concreta devem ser preservadas as verificações advindas do modelo full function. Caso os dados recebidos pela operação satisfaçam essas restrições, deve-se proceder com a efetivação do serviço. No modelo de implementação dos serviços, a transição do estado na operação jc_operation é representada pela linha result ← jc_cvar.concrete_operation (jc_param). 4.3.3 Implementação B do applet Java Card A especificação da classe applet é desenvolvida de forma independente do modelo dos serviços da aplicação. Nessa abordagem, o módulo JC_Applet_r (figura 4.9) é um refinamento direto da especificação que modela a classe applet da API Java Card. REFINEMENT JC_Applet_r REFINES Applet SEES JByte, JShort, Apdu_Properties OPERATIONS install(bArray, bOffset, bLength) = BEGIN skip END; process(apdu) = PRE apdu ∈ APDU THEN skip END; res ← select = CHOICE res := FALSE OR res := TRUE END; deselect = BEGIN skip END; (...) END Figura 4.9: Refinamento da máquina Applet da API Java Card. É importante observar que uma operação de uma máquina não pode requisitar outra operação da mesma máquina em seu corpo. Contornar essa limitação foi uma das motivações para separar o modelo do applet, que refina a máquina Applet da API, do modelo dos serviços que a aplicação Java Card oferece. Nessa proposta, a implementação JC_Applet_i (figura 4.10) importa a especificação JC_App, dessa forma tornando possível que as operações da especificação applet, tais como install, process, select e deselect, referenciem os métodos de serviço sem interferir em seu estado interno. Visando uma melhor distribuição de responsabilidades entre o desenvolvimento dos serviços e do applet, recomenda-se que as operações da API necessárias para a recepção e envio de 67 dados através do buffer apdu sejam feitas pelo componente applet. Nesse caso, as informações do buffer são decodificadas e enviadas aos parâmetros dos serviços, de modo semelhante ao que é feito na operação process da figura 4.10. IMPLEMENTATION JC_Applet_i REFINES JC_Applet_r SEES JByte, JShort, Apdu_Properties, JC_Context, InterfaceContext IMPORTS app.JC_App, apdu.Apdu OPERATIONS install(bArray, bOffset, bLength) = BEGIN skip END; process(apdu) = VAR buffer, value, out IN buffer ← apdu.getBuffer; value := jc_of_apdu(buffer); IF jc_pre(jc_of_apdu(buffer)) = TRUE THEN out ← app.jc_operation (value) END END; res ← select = BEGIN res := TRUE END; deselect = BEGIN skip END; (...) END Figura 4.10: Implementação B do applet Java Card. Destacam-se ainda outros benefícios da separação entre os modelos do applet e dos serviços, a saber: 1. Traduzir a classe applet a partir do refinamento da sua especificação em B; 2. Evitar que o modelo concreto dos serviços contenha mais operações que o modelo abstrato, caso os métodos do applet fossem diretamente especificados no primeiro; 3. Diminuir a intervenção da ferramenta de tradução, que na versão inicial deste trabalho era a responsável por inserir o código dos métodos do applet. Na versão atual, a tradução é feita diretamente a partir das operações correspondentes da classe applet. 4.4 Verificação da Mudança de Interface entre os Modelos Java e Java Card Cumpre ressaltar que foi preciso criar meios para tratar as limitações do refinamento em B, tendo em vista possibilitar a verificação do modelo concreto da especificação Java Card quando 68 há mudança de interface em relação ao componente abstrato. No método B, não é possível modificar a interface da especificação em um refinamento posterior, mesmo que, intuitivamente, ainda haja equivalência entre os modelos abstratos e concretos. Em uma operação, isso inclui a representação dos tipos de dados, a ordem e a quantidade dos seus parâmetros de entrada e saída. Observa-se que, em alguns casos, a mudança na interface concreta faz-se necessária, com o objetivo de compatibilizar esse modelo com as limitações de Java Card ou representar a recepção e envio de informações através do buffer adpu. Por exemplo, no caso da representação do tipo inteiro (int) de Java, uma vez que não é garantida a sua implementação em Java Card, deve-se representá-lo, no modelo concreto, como dois valores short ou como um array de bytes. É importante destacar que, no formalismo Event-B, contribuição recente derivada em parte do método B, a interface entre as operações refinadas pode ser modificada. Por outro lado, conforme discutido no capítulo 3 (seção 3.3), Event-B não é adequado ao desenvolvimento de software conforme proposto neste trabalho. Desse modo, preferiu-se continuar a utilizar o método B tradicional, buscando soluções para verificar o “refinamento” de módulos com interface distinta. A seção 4.4.1 apresenta uma abordagem, desenvolvida nesta tese, que corresponde a uma forma de composição de módulos que resolve o problema da adaptação de interface sem a necessidade de se desviar do refinamento clássico de B. A título de ilustração, na seção 4.7, demonstra-se o uso da técnica de retrenchment [BP98], solução inicial pesquisada para contornar a restrição de mudança de interface. Trata-se de uma variante menos rigorosa do refinamento tradicional, e, por esse motivo, não foi incorporada ao método. 4.4.1 Um método de “refinamento” para permitir a mudança de interface Nesta seção apresenta-se uma técnica, desenvolvida na presente tese, para permitir a verificação da mudança de tipos e de interface entre um módulo abstrato e seu correspondente concreto, sem no entanto sair do escopo do método B tradicional. Diante da impossibilidade de se verificar diretamente que a máquina Java Card (JC_App) é um refinamento da especificação Java inicial (J_App), o método proposto define um padrão de adaptação de interface a partir do refinamento do modelo abstrato. Para tanto, as operações refinadas devem manter a interface abstrata e as transições de estado em seu corpo devem ser feitas por meio das operações correspondentes do componente concreto (JC_App). Caso esse processo verifique-se correto, através da verificação das obrigações de prova do refinamento, 69 então é possível atestar que esses modelos são equivalentes. O módulo J_ App_r (figura 4.11) representa o modelo a ser seguido para utilizar a técnica proposta. Para tanto, deve-se refinar a máquina abstrata Java (REFINES J_App) e incluir a especificação concreta (INCLUDES JC_App). No invariante, deve-se relacionar os estados desses modelos através de um invariante de ligação. No caso geral, em que a representação dos tipos concretos e abstratos é distinta, são utilizadas funções para converter o estado concreto no estado abstrato correspondente (na figura, j_v = java_of_jc ( jc_v )). Ressalta-se que deve ser fornecida, para cada tipo relacionado, uma função que faz o trabalho inverso, ou seja, que converte o estado abstrato em seu correspondente concreto (como jc_of_java (jc_param), no corpo da operação). Nesse caso, as funções são bijetoras, sendo uma inversa da outra. REFINEMENT J_ App_r REFINES J_App SEES J_Context, JC_Context, InterfaceContext INCLUDES JC_App INVARIANT j_v = java_of_jc ( jc_v ) OPERATIONS j_r ← j_operation ( j_param) = PRE j_param ∈ J_TYPE ∧ j_pre ( j_param) = TRUE THEN VAR tmp IN tmp ← jc_operation ( jc_of_java ( j_param)) ; j_r := java_of_jc (tmp) END END END Figura 4.11: Refinamento da especificação abstrata inicial para verificar a correção da mudança de interface pelo módulo Java Card. Observa-se que as operações de J_App_r possuem a mesma assinatura que o componente abstrato, dessa forma possibilitando o refinamento. A adaptação é feita no corpo da operação refinada, em um processo de três passos, para o caso de uma operação que possui parâmetros de entrada e retorno de dados: 1. Inicialmente, o valor armazenado nos parâmetros abstratos (j_param) é convertido em um valor equivalente no tipo concreto (jc_of _java(j_param)); 2. Posteriormente, a operação concreta é aplicada sobre os dados concretos tmp ← jc_operation( jc_of _java(j_param)); 3. Por fim, o resultado da aplicação da operação Java Card é convertido para o tipo abstrato e retornado (j_r := java_of _jc(tmp)). 70 As funções de conversão utilizadas para efetuar a relação de refinamento entre os estados abstratos e concretos são definidas na máquina InterfaceContext (figura 4.12). Essas funções, uma vez definidas e verificadas, podem ser reutilizadas em outros desenvolvimentos. Como trabalhos futuros, deve-se prover uma versão do módulo InterfaceContext contendo um conjunto verificado de funções de conversão entre os tipos de dados mais comuns, por exemplo, entre tipos básicos e arrays de bytes. O módulo InterfaceContext também contém alguns corolários na cláusula ASSERTIONS que especificam propriedades adicionais para facilitar o trabalho de verificação do desenvolvimento. MACHINE InterfaceContext SEES JInt , JCInt CONSTANTS java_of_jc, jc_of_java PROPERTIES java_of_jc ∈ JC_TYPE JAVA_TYPE ∧ jc_of_java ∈ JAVA_TYPE JC_TYPE ∧ jc_of_java −1 = java_of_jc ASSERTIONS java_of_jc −1 = jc_of_java ∧ dom ( java_of_jc ) = JC_TYPE ∧ dom ( jc_of_java ) = JAVA_TYPE END Figura 4.12: A máquina InterfaceContext. Destaca-se também que, nesta proposta, uma vez que se tenha provado a equivalência dos componentes abstratos e concretos para alguma representação de tipo, obtidas através da correta verificação do refinamento J_App_r, torna-se possível reusar a abordagem para esses tipos em outros desenvolvimentos semelhantes, sem a necessidade de verificá-los novamente. Ressalta-se que, na versão atual do método, o desenvolvimento desta etapa é feito de forma manual. Pretende-se automatizar esse processo através de uma ferramenta que gere (ao menos em parte) o padrão de refinamento proposto a partir dos modelos Java e Java Card. 4.5 Geração da API para a Aplicação Host Inicialmente, cumpre observar que um primeiro protótipo desta parte do trabalho foi desenvolvido por Moraes [Mor07]. Na tese, a solução anterior foi totalmente remodelada visando otimizar o reuso de código. Além disso, a implementação, antes desenvolvida em Java, foi refeita utilizando a linguagem C++ e a API BCompiler [Cle12b], que fornece acesso aos elementos do código B, tornando possível a tradução para o código Java correspondente. Maiores detalhes acerca do B Compiler podem ser encontrados na seção 4.6. 71 Conforme discutido na seção anterior, o desenvolvimento da aplicação do lado cartão segue todo o processo de especificação e refinamento em B, sendo o applet gerado a partir do último nível de refinamento, denominado de implementação. No caso da geração de código da API para o lado host, na versão atual, decidiu-se gerá-lo apenas com base na máquina de especificação inicial, em um processo totalmente automatizado. Isso deve-se ao fato das informações contidas na assinatura de cada operação B que especifica um serviço correspondente no cartão serem suficientes para a geração dos componentes de auxílio ao cliente. Dessa forma, é possível obter informações sobre cada parâmetro que será enviado, assim como os valores de retorno da operação. Assim como apresentado no capítulo 2, para o desenvolvimento da aplicação host existem APIs que oferecem todos os recursos necessários para se estabelecer a comunicação com o cartão e acessar seus serviços. Neste trabalho, é possível a geração de classes de comunicação com o cartão para as APIs Smart Card I/O e RMI Client. Essa última é uma API bastante simples, parte da especificação Java Card padrão, exclusiva para comunicação com applets RMI. Na versão inicial do trabalho, também encontrava-se implementada a API OpenCard Framework. No entanto, em virtude da sua descontinuidade, ela não foi incluída na versão atual do trabalho. Ressalta-se que o desenvolvedor pode facilmente fornecer a implementação para uma nova API de sua preferência, desde que implemente duas interfaces detalhadas a seguir nesta seção. Cumpre destacar que o objetivo deste ramo do desenvolvimento do método é a geração de um conjunto de classes (fig. 4.13) que possibilitam a comunicação de forma transparente com a aplicação cartão. Sendo assim, livra-se o usuário da tarefa tediosa e propensa a erros de manipular os detalhes de baixo-nível do sistema smart card. Para tanto, esses componentes gerenciam a conexão com os serviços da aplicação cartão, bem como a codificação e decodificação das informações enviadas e recebidas pelo applet. Dessa forma, o usuário da API é responsável apenas pela especificação dos aspectos funcionais da aplicação, tornando o desenvolvimento mais ágil e produtivo. É importante observar que a proposta não é a geração da própria aplicação host, uma vez que não há como prever a lógica de negócio e a interface da aplicação principal, mas sim de um conjunto de classes que efetua todo o trabalho de comunicação com o cartão. Portanto, o que se oferece é uma API contendo a abstração da chamada aos serviços oferecidos pelo cartão e classes que implementam rotinas de tratamento de dados, como, por exemplo, conversões de tipos entre o universo Java padrão da aplicação host e o universo Java Card do lado cartão. O diagrama da figura 4.13 ilustra as classes e interfaces componentes da API. Dentre elas, destacam-se duas classes essenciais para a comunicação com o applet, e que são geradas para 72 Figura 4.13: Diagrama de classes UML descrevendo os componentes da API para a aplicação cliente. cada nova aplicação, denominadas de AppCommunication e AppProxy. É importante observar que os demais componentes podem ser reutilizados em diversos projetos, sem a necessidade que sejam gerados novamente a cada execução da aplicação de geração da API. Esse aprimoramento foi feito durante o trabalho da tese, após uma nova modelagem das classes da abordagem inicial desenvolvida por Moraes [Mor07]. Ressalta-se que a classe AppCommunication é diretamente acessada pela aplicação host e contém métodos Java de alto nível para requisitar cada serviço do applet. No caso de applets APDU, as chamadas aos serviços são encaminhadas para a classe AppProxy, que é a responsável por codificar os dados recebidos em um pacote no formato do comando APDU e enviá-lo ao applet. O componente AppProxy também decodifica a resposta retornada, enviando-a de volta à classe AppCommunication. Observa-se que esse componente não é necessário para a geração da API para o applet RMI, uma vez que, nesse caso, a comunicação é estabelecida em um nível mais alto que aquele para a aplicação APDU. A inclusão de uma nova API de comunicação com o smart card é bastante simples, sendo necessário apenas implementar as interfaces I_APDUComm e I_CardConnector. Ressalta-se que, neste trabalho, é fornecida a implementação das interfaces para a API Smart Card I/O. A primeira, referenciada diretamente pela classe Proxy, descreve os métodos que devem ser implementados para enviar e/ou receber as respostas advindas do componente cartão através de uma API específica. A conexão efetiva com a API é realizada por intermédio da implementação da interface I_CardConnector. Nesse caso, deve-se ao menos desenvolver os métodos para se conectar ao cartão (cardInit) e obter a referência ao objeto que representa o cartão (getCardRe- 73 ference). Os demais métodos se referem ao que deve ser feito no momento em que o cartão é inserido (cardInserted) e sempre que a seção de comunicação for interrompida (cardRemoved). Ainda em relação aos componentes da API, destaca-se a classe HostUtil, que possui métodos úteis às tarefas de conversão de dados entre as aplicações host e cartão, efetuadas na classe AppProxy. Esses métodos permitem inserir valores byte, short, int e array no buffer apdu, assim como converter o array de byte da resposta APDU em um dos tipos primitivos de Java. 4.5.1 Exemplo de classes geradas Nesta seção apresentam-se as classes AppCommunication e AppProxy geradas para a máquina Transport, cujo código Java Card correspondente foi detalhado na seção 2.3. As operações da especificação Transport (figura 4.14) exemplificam o envio de um valor short para o cartão (addCredit), o envio de um comando sem parâmetros e sem retorno, no caso o débito de uma unidade de crédito (debit), e, por fim, a recepção de um valor short (getBalance). Em addCredit, adicionou-se o parâmetro (ct) contendo o tipo do cartão, apenas para demonstrar o envio de mais de um parâmetro. MACHINE Transport SEES Transport_Constants, JShort VARIABLES balance, card_type INVARIANT balance ∈ JSHORT ∧ balance ≥ 0 ∧ card_type ∈ CARD_TYPES ∧ (card_type = gratuitous_card =⇒ balance = 0) INITIALISATION balance := 0 || card_type :∈ CARD_TYPES OPERATIONS addCredit (cr, ct) = PRE cr ∈ JSHORT ∧ ct ∈ JSHORT ∧ cr > 0 ∧ card_type 6= gratuitous_card ∧ sum_short(balance, cr) ≥ 0 ∧ sum_short(balance, cr) ≤ MAXSHORT THEN balance := sum_short(balance, cr) END ; debit = PRE card_type 6= gratuitous_card ∧ subt_short(balance, 1) ≥ 0 THEN balance := subt_short(balance, 1) END; res ← getBalance = BEGIN res := balance END END Figura 4.14: Especificação abstrata Transport. A figura 4.15 exibe a classe TransportCommunication, criada de forma automatizada com o auxílio da ferramenta de geração da API (seção 4.6.1), a partir da assinatura das operações da máquina Transport. Perceba que ela fornece, à aplicação que irá utilizar a API, uma interface Java de alto nível, encapsulando todos os aspectos de conversão de dados e da comunicação com o cartão nos demais componentes. Os valores recebidos da classe TransportCommunication são tratados pela classe TransportProxy (figuras 4.16 e 4.17), tendo em vista enviá-los ao cartão através da implementação da 74 public c l a s s TransportCommunication { byte [ ] appletAID = nu ll ; TransportProxy cardProxy = null ; p u b l i c T r a n s p o r t C o m m u n i c a t i o n ( b y t e [ ] a i d ) throws E x c e p t i o n { appletAID = aid ; c a r d P r o x y = new T r a n s p o r t P r o x y ( a p p l e t A I D ) ; } public void a d d C r e d i t ( short cr , short c t ) { cardProxy . a d d C r e d i t ( cr , c t ) ; public void d e b i t ( ) { cardProxy . debit ( ) ; } public short getBalance ( ) { return cardProxy . getBalance ( ) ; } } } Figura 4.15: Classe TransportCommunication. interface I_APDUComm para uma API específica. No caso do exemplo, implementou-se a API smart card I/O (apdu = new SmartCardIOAPDUComm(aid)). Na classe proxy, todo parâmetro de tipo primitivo é convertido em um valor byte ou em um array de bytes, através das funções de conversão da classe HostUtil. Como exemplo, tem-se o método addCredit, que necessita enviar dois valores short ao cartão. O primeiro, a quantidade de créditos (cr), é convertido em dois bytes, armazenados nas variáveis locais p1 e p2 (linhas 11 e 13). Por outro lado, o tipo do cartão (ct) é convertido em um array de byte (2 bytes) e armazenado na variável data. O efetivo envio dos parâmetros é feito através de um dos métodos sendCommand da interface I_APDUComm (linha 17). No caso, deve-se informar a classe de instrução (APPLET_CLA), o código do serviço requisitado (addCredit_INS) e os parâmetros que serão enviados em p1, p2 e no campo data do comando apdu. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class TransportProxy { s t a t i c f i n a l b y t e APPLET_CLA = ( b y t e ) 0 x80 ; s t a t i c f i n a l b y t e a d d C r e d i t _ I N S = ( b y t e ) 0 x10 ; p r i v a t e I_APDUComm apdu ; (...) p u b l i c T r a n s p o r t P r o x y ( b y t e [ ] a i d ) { apdu = new SmartCardIOAPDUComm ( a i d ) ; } public void a d d C r e d i t ( short cr , short ct ) { b y t e [ ] d a t a = new b y t e [ 1 2 7 ] ; int index = 0; byte [ ] sh = H o s t U t i l . sho rt ToB yt eA rra y ( c r ) ; b y t e p1 = ( b y t e ) s h [ 0 ] ; b y t e p2 = ( b y t e ) s h [ 1 ] ; HostUtil . shortToByteArray ( data , ct , index ) ; i n d e x += 2 ; apdu . sendCommand ( APPLET_CLA , a d d C r e d i t _ I N S , p1 , p2 , d a t a ) ; } (...) Figura 4.16: Classe TransportProxy - operação addCredit. É importante ressaltar que o procedimento de envio de informações nos campos p1 e p2 é comumente adotado em um desenvolvimento Java Card, tendo em vista otimizar o espaço de armazenamento. Nessa abordagem, sempre que, nos primeiros parâmetros a serem enviados, há um ou dois valores de até dois bytes (byte, boolean ou short), eles serão inseridos em p1, p2 ou 75 em ambos. Para os demais casos, os valores são armazenados diretamente no campo data, um após o outro, na sequência em que aparecem na assinatura da operação. O controle da posição de inserção do próximo valor no buffer é feito pela variável index, que inicialmente aponta para a posição 0 (zero), sendo incrementada de acordo com o tamanho do último valor armazenado. A recepção de valores através do buffer deve ser solicitada por um dos métodos sendCommandAndGetResponse, como no caso da operação getBalance (figura 4.17), que espera receber um valor short correspondente à quantidade de créditos armazenados no cartão. No exemplo, um array de bytes é recebido (linha 5), e, posteriormente, é convertido em um short e retornado para a operação correspondente da classe communication (linha 8), que por sua vez encaminha o valor à aplicação cliente. Finalmente, no caso da operação debit, em que não há envio e recepção de informações, o comando apdu enviado apenas requisita a execução do método da classe applet, responsável por debitar o valor de uma unidade de crédito do cartão. 1 2 3 4 5 6 7 8 9 10 public void d e b i t ( ) { apdu . sendCommand ( APPLET_CLA , d e b i t _ I N S , ( b y t e ) 0 , ( b y t e ) 0 ) ; } public short getBalance ( ) { b y t e [ ] r e s = apdu . sendCommandAndGetResponse ( APPLET_CLA , getBalance_INS , ( byte ) 0 , ( byte ) 0 , 2 ) ; return HostUtil . byteArrayToShort ( r e s ) ; } } Figura 4.17: Classe TransportProxy - operações debit e getBalance. 4.6 Suporte de Ferramentas ao Método BSmart O suporte de ferramentas ao BSmart é composto por um conjunto de programas (seção 4.6.1) implementados na linguagem C++, que possibilitam a verificação sintática e de tipos em um módulo B, a geração de refinamentos e a tradução de B para Java ou Java Card. Nas suas versões iniciais, desenvolvidas durante o mestrado do autor desta tese, a distribuição desses componentes foi efetivada na forma de plugins para a IDE Eclipse [Ecl10], escolhida devido a possibilitar o desenvolvimento ágil da interface com o usuário e a fácil distribuição das ferramentas. No entanto, na versão atual, após a reengenharia do conjunto de ferramentas, optou-se por fornecê-las como aplicações independentes, que podem ser executadas em linha de comando, ou podem ser acopladas à IDE fornecida pela ferramenta Atelier B [Cle12a]. Observa-se que, na versão inicial dos programas, utilizou-se o software JBtools [Voi02] para, através de uma biblioteca fornecida por essa ferramenta, ter acesso à árvore sintática dos elementos da especificação e realizar as transformações de código para B ou Java. Apesar de 76 fornecer um meio ágil de desenvolvimento, a ferramenta JBtools [Voi02] apresenta alguns problemas. Dentre eles, pode-se destacar a exibição de mensagens de erro pouco esclarecedoras, o tratamento inadequado da tradução de tipos para Java Card e a compatibilidade apenas com a notação clássica da linguagem B, tal qual descrita no B-Book [Abr96]. Cumpre destacar que, ao invés de se modificar o JBtools ou criar um novo conjunto de software a partir do zero, buscou-se uma outra API para a manipulação de máquinas e refinamentos B. A solução encontrada foi a API BCompiler [Cle12b], que possui funcionalidade semelhante ao JBtools, com as vantagens de ser sólida, estável e amplamente compatível com o Atelier B [Cle12a], atualmente a ferramenta de referência para desenvolvimento com o método B. Ressalta-se que a gramática B definida pelo Atelier B inclui construções que não são suportadas pelo B clássico (struct e record, por exemplo) e apresenta divergências quanto à forma sintática de alguns elementos. Nesse sentido, além de eliminar os problemas apresentados pelo JBtools, a migração das ferramentas do BSmart para o BCompiler possibilita a sua integração com o ambiente de desenvolvimento fornecido pelo Atelier B. Essa abordagem, além de mais robusta que a anterior, proporciona maior agilidade e comodidade ao especificador, que possui, em uma mesma IDE, todo o ambiente necessário para o desenvolvimento Java Card com o método BSmart. Cumpre enfatizar que o software BCompiler é utilizado em todos os componentes para manipular um módulo B tendo em vista geração de código para um módulo B (caso do refinamento full function) ou para Java ou Java Card. Para tanto, antes de ser realizado o propósito de cada ferramenta, é feita a verificação sintática e de tipos do módulo fornecido como entrada através de chamadas a funções fornecidas pelo BCompiler. A resolução bem sucedida dessas verificações iniciais gera a árvore de parser para o módulo, tornando possível o acesso a cada elemento desse. 4.6.1 Ferramentas e APIs Desenvolvidas Os programas para suporte ao método encontram-se representados no diagrama de classes da figura 4.18 e são descritos a seguir. Cumpre destacar que todos eles incluem a biblioteca AMNPrinter, também desenvolvida nesta tese. Ela fornece um método para cada construção que pode ser traduzida a partir de um módulo B, possibilitando a impressão, em um arquivo de texto, do seu código em B (AMNPrinterB) ou Java (AMNPrinterJava ou AMNPrinterJavaCard). 77 Figura 4.18: Ferramentas de suporte ao método BSmart. ff_gen: Geração de módulos Java Card Possibilita a criação do refinamento full function e da sua máquina de contexto, conforme descrito na seção 4.4.1. Deve-se fornecer como entrada a máquina abstrata que inicia o desenvolvimento ou um refinamento dela. Para cada restrição encontrada na pré-condição de uma operação é gerada uma constante na máquina de contexto, correspondente à exceção a ser lançada, formada por um prefixo seguido do nome da operação e de um número de sequência (por exemplo, ex_op_1, ex_op_2). Após gerado, a correção do refinamento pode ser atestada por uma ferramenta de suporte ao método B, tal qual o Atelier B, através da verificação de sintaxe, tipos e de obrigações de prova. b2java_gen: Tradução de B para Java/Java Card Traduz um ou mais módulos de imple- mentação B em seu código Java ou Java Card correspondente. É possível gerar como saída uma classe applet Java Card, Java Card ou Java. No primeiro caso, trata-se de uma classe Java Card que herda da classe Applet da API. As únicas intervenções do gerador são a inclusão dessa informação de herança (extends javax.framework.Applet) e de importações (import) de classes da API. Ressalta-se que o capítulo 5 detalha a o processo de tradução das construções presentes no modelo de implementação em B para as construções equivalentes na sua classe Java Card correspondente. Para tanto, utilizou-se o formalismo ASF+SDF [BKV08] com o suporte da ferramenta Meta-Environment [ME09]. Apresentam-se também, sempre que necessário, as decisões de projeto que foram estabelecidas para permitir a correta geração de código. Observa-se que, no caso da tradução para Java Card, a ferramenta verifica se os módulos de implementação satisfazem as restrições impostas pela versão 2.2.2. Tendo em vista possibilitar a correta geração de código, são verificadas as propriedades a seguir descritas na especificação Java Card [Ora11]: 78 • Uso do tipo inteiro (int), que até a versão Java Card 2.2.2 é de implementação opcional. • Um módulo B pode ter no máximo 255 constantes e variáveis especificadas, incluindo aquelas declaradas em módulos vistos (cláusula sees) ou inclusos (cláusula includes). • Um módulo B pode declarar no máximo 255 operações que sejam traduzidas em métodos públicos (public) ou protegidos (protected). Na tradução desenvolvida neste trabalho, todos os métodos de serviço da aplicação Java Card são traduzidos com visibilidade pública. A única exceção é o método construtor da classe applet, gerado com visibilidade privada (private), uma vez que ele deve ser invocado somente dentro da classe, no corpo do método init, para instanciação e registro do applet junto ao ambiente de execução. • O número máximo de variáveis locais e parâmetros que podem ser declarados em uma operação é de 255. • Uma classe Java Card deve declarar apenas arrays de uma dimensão. Em B, um array corresponde a uma função total mapeada de um intervalo inteiro para um conjunto ou a uma sequência (seq) sobre um dado conjunto. host_gen: Geração da API para a aplicação cliente Utiliza a biblioteca de tradução Java (AMNPrinterJava) para gerar os componentes App_Communication e App_Proxy da API de suporte para a aplicação host. Conforme descrito na seção 4.5, os demais componentes da API, referenciados pelas duas classes geradas, não necessitam ser gerados para cada aplicação, sendo fornecidos junto à ferramenta. 4.7 Uso de Retrenchment para a verificação da mudança de interface Na seção 4.4.1 descreveu-se uma técnica para possibilitar a verificação entre modelos abstratos e concretos com interface distinta. Através de um refinamento alternativo de adaptação de interfaces, foi possível que o método BSmart continuasse dentro do escopo de verificação com o método B. Nesta seção apresenta-se uma solução alternativa, utilizando a teoria de retrenchment, que foi inicialmente aplicada visando contornar essa restrição de B. Uma vez que a abordagem desenvolvida na tese é mais rigorosa, por continuar na teoria de refinamento em B, preferiu-se incorporá-la ao método em detrimento ao retrenchment. 79 A técnica denominada retrenchment foi proposta por Banach e Poppleton [BP98], com a motivação de estender a aplicabilidade do desenvolvimento formal para uma gama maior de aplicações. O retrenchment impõe menos restrições, quando comparado ao refinamento tradicional, devido a flexibilizar a conexão entre as operações abstratas e concretas. Ressalte-se que, utilizando-se essa técnica, pode-se especificar e verificar situações que não seriam permitidas ao refinamento. No retrenchment, é possível fortalecer a pré-condição de uma operação e/ou enfraquecer a sua pós-condição. Dessa forma, torna-se possível modificar a interface de uma operação, ou transferir parte do comportamento das variáveis de estado para variáveis de entrada e saída, ou vice-versa. Por outro lado, os mecanismos que permitem essa flexibilização, também tornam o retrenchment menos rigoroso em relação ao refinamento tradicional, conforme será discutido posteriormente nesta seção. No artigo [BP98], os autores apresentam a teoria do retrenchment e a sua aplicação como uma extensão do método formal B. A seguir, introduz-se os conceitos relacionados a essa técnica aplicada a B e, posteriormente, a sua utilização para especificar a mudança de tipos e interface das operações durante o processo de desenvolvimento no método BSmart. Retrenchment em B Observa-se que o retrenchment é uma máquina B com a adição de alguns outros componentes (figura 4.19), a saber, uma (1) cláusula RETRENCHES, contendo o nome da máquina ou refinamento objeto do retrenchement, uma (2) cláusula RETRIEVES que especifica a relação entre os estados abstratos e concretos e (3) uma substituição generalizada composta pelas cláusulas LVAR, WITHIN e CONCEDES, que estende a substituição no corpo de uma operação, especificando as situações nas quais a operação concreta falha ao refinar a operação abstrata. Percebe-se que, diferentemente do refinamento em B, onde o invariante local e a relação entre os estados abstratos e concretos são ambos especificados no invariante do refinamento, no retrenchment, o invariante somente se aplica ao estado concreto. Todos os elementos que descrevem um refinamento B estão disponíveis no retrenchment. Evidencia-se que podem ser utilizadas, do mesmo modo que uma máquina B comum, a cláusula de definição de conjuntos (SETS) e todas as cláusulas utilizadas na composição de máquinas. Com relação às clausulas introduzidas pelo retrenchment, observa-se que a cláusula LVAR é opcional e deve ser utilizada apenas se for necessário declarar variáveis cujo escopo são as cláusulas WITHIN e CONCEDES. Uma vez declaradas, essas variáveis devem receber tipo e restrições aos seus valores em WITHIN. 80 MACHINE MA (pmA ) CONSTRAINTS PA (pmA ) ... VARIABLES vA INVARIANT Inv(vA ) INITIALISATION Init(vA ) OPERATIONS rA ←− OPA (pA ) = SA (vA , pA , rA ) END MACHINE MR (pmR ) RETRENCHES MA (pmA ) CONSTRAINTS PR (pmR ) ... VARIABLES vR INVARIANT Inv(vR ) RETRIEVES Ret(vA , vR ) INITIALISATION Init(vR ) OPERATIONS rR ←− OPR (pR ) = BEGIN SR (vR , pR , rR ) LVAR R WITHIN W (pA , pR , vA , vR , R) CONCEDES C(vA , vR , rA , rR , R) END END Figura 4.19: Máquina B padrão (esquerda) e máquina retrenchment (direita). Evidencia-se que a cláusula WITHIN também pode ser utilizada, se necessário, para especificar o fortalecimento da pré-condição de uma operação. Por sua vez, a cláusula CONCEDES presta-se ao papel de enfraquecer a pós condição de uma operação. Essas cláusulas adicionais podem ser precisamente descritas e compreendidas através da definição das obrigações de prova do retrenchment, o que será feito na próxima seção. Posteriormente, ilustrar-se-á a aplicação do retrenchment ao desenvolvimento BSmart de modo a permitir a verificação da mudança no tipo e na interface das operações da máquina inicial para a sua representação Java Card. Obrigações de prova As obrigações de prova do retrenchment podem ser classificadas como locais, ao se lidar apenas com informações do módulo de retrechment, ou conjuntas, no momento em que relacionam a máquina de retrenchment com o módulo B abstrato. É interessante observar que as obrigações de prova locais em um módulo de retrenchment são as mesmas que aquelas encontradas em uma máquina B comum, a saber, o estabelecimento do invariante na inicialização e a preservação do estado especificado no invariante pelas opera- 81 ções, quando elas são aplicadas a estados nos quais a pré-condição da operação é satisfeita. A verificação dessas obrigações de prova garante a consistência interna do módulo. Ao tratar-se das obrigações de provas conjuntas, a inicialização é similar ao que é encontrado no refinamento tradicional (seção 3.2.1), exceto pelo fato de que se deve satisfazer o predicado na cláusula RETRIEVES, ao invés do INVARIANT. A obrigação de prova que deve ser verificada na inicialização do retrenchment é definida pela fórmula: Obrigação de Prova 4.1 PA (pmA ) ∧ PR (pmR ) ⇒ [Init(vR )](¬[Init(vA )](¬Ret(vA , vR ))) As obrigações de prova para as operações são as que realmente definem o retrenchment. Como salientado no antecedente da fórmula da obrigação de prova 4.2, para cada operação, a validade do processo de verificação é condicionada às situações em que as propriedades especificadas para os parâmetros do módulo (PA (pmA ) e PR (pmR )), os invariantes abstratos (Inv(vA )) e concretos (Inv(vR )) e a relação de “retrenchment” (Ret(vA , vR )) são satisfeitas. Essas condições são similares ao que é feito para o refinamento tradicional. Por outro lado, de forma diferente do refinamento, no qual todo estado que pode ser atingido pelo modulo abstrato deve ser capaz de ser atingido pelo seu correspondente concreto, no retrenchment são as operações concretas que condicionam o processo de verificação. Obrigação de Prova 4.2 PA (pmA ) ∧ PR (pmR ) ∧ (Inv(vA ) ∧ Ret(vA , vR ) ∧ Inv(vR ))∧ trm(SR (vR , pR , rR )) ∧W (pA , pR , vA , vR , R)) ⇒ trm(SA (vA , pA , rA ))∧ [SR (vR , pR , rR )](¬[SA (vA , pA , rA )](¬(Ret(vA , vR ) ∨C(vA , vR , rA , rR , R)))) Destaca-se que a prevalência dos estados concretos sobre os abstratos é a característica do retrenchment que permite o comportamento mais permissivo quando comparado ao refinamento tradicional. Enquanto agrega a vantagem ao retrechment de especificar uma operação no módulo concreto de forma mais flexível que no refinamento, essa característica o enfraquece, tornando-o uma abordagem de desenvolvimento menos rigorosa que o refinamento tradicional. Assim, no retrenchment, é possível existirem estados abstratos que não possam ser simulados no componente concreto. Isso aparece na obrigação de prova, com a terminação da operação concreta (trm(SR (vR , pR , rR ))) na hipótese e a terminação da operação abstrata (trm(SA (vA , pA , rA ))) apenas no consequente. 82 Aspectos relacionados ao estado abstrato que não estão expressos na relação de retrenchment (Ret(vA , vR )) podem ser especificados na cláusula WITHIN (W (pA , pR , vA , vR , A)), sendo também o lugar de se relacionar as entradas de uma operação abstrata com a sua correspondente concreta, no caso, por exemplo, da mudança na interface entre elas. Por sua vez, no consequente da obrigação de prova tem-se o predicado da cláusula CONCEDES (C(v_A, v_R, r_A, r_R, R)). Ele permite especificar comportamentos adicionais relacionados aos estados concretos e abstratos e aos valores de saída da operação em situações nas quais a relação de retrenchment não seja satisfeita. Na obrigação de prova, esse comportamento é inserido através da disjunção do predicado concedes com o predicado da relação de retrenchment (Ret(v_A, v_R) ∨C(v_A, v_R, r_A, r_R, R)). Retrenchment aplicado ao desenvolvimento Java Card A figura 4.20 é uma representação de um retrenchment da especificação inicial (J_App) contendo apenas tipos compatíveis com Java Card do desenvolvimento BSmart. O componente que representa o estado do módulo é a variável jc_v, restrita ao sistema de tipos Java Card, o que é estabelecido no invariante, assim como feito para uma máquina B clássica. Na cláusula RETRIEVES do retrenchment, é relacionado o estado abstrato (j_v) da máquina J_App com o estado concreto do retrenchment, armazenado na variável (jc_v). Para tanto, é utilizada a função de conversão java_of_jc, definida no componente InterfaceContext (seção 4.4.1), que associa o estado Java Card ao estado Java equivalente. MACHINE JC_App_ret RETRENCHES J_App SEES J_Context, JC_Context, InterfaceContext ABSTRACT_VARIABLES jc_v INVARIANT jc_v ∈ JC_TYPE ∧ jc_inv ( jc_v ) = TRUE RETRIEVES j_v = java_of_jc ( jc_v ) INITIALISATION ANY val WHERE val ∈ JC_TYPE ∧ jc_inv ( val ) = TRUE THEN jc_v := val END OPERATIONS jc_r ← jc_operation ( jc_param ) = BEGIN PRE jc_param ∈ JC_TYPE ∧ jc_pre ( jc_param ) = TRUE THEN jc_v := jc_stf ( jc_v , jc_param ) k jc_r := jc_ouf ( jc_v , jc_param ) END WITHIN j_v := java_of_jc (jc_v ) END Figura 4.20: Retrenchment Java Card da especificação abstrata principal J_App (Fig. 4.2). 83 No caso particular do retrenchment Java Card apresentado, a função de conversão de tipos Java para Java Card é também inserida na cláusula WITHIN para possibilitar a verificação da equivalência entre os estados abstratos e concretos. A substituição (j_v := java_of_jc(jc_v)) evidencia que o retrenchment é válido, desde que ele e a máquina abstrata (J_App) sejam capazes de efetuar transições de estado equivalentes, ainda que a interface e os tipos de dados recebidos pela operação do retrenchment tenham sido modificados. Embora o retrenchment possa ser uma alternativa ao refinamento clássico, possibilitando desenvolvimentos que não seriam possíveis nesse modelo, ele possui como desvantagem o fato de permitir a verificação menos rigorosa que o refinamento. Além disso, a sua utilização ainda é inexpressiva e não há suporte de ferramentas maduro para ele. Nessa direção, uma iniciativa acadêmica é software Frog [FB07], desenvolvido como parte da tese de Frasier [Fra08]. Essa ferramenta propõe um framework para automatizar o suporte ao retrenchment. Inicialmente, o método formal Z [Spi92] é empregado como notação matemática para a especificação. Obrigações de prova são geradas no formato do provador de teoremas Isabelle [NPW10], que então efetua as verificações. Ressalta-se que o retrenchment Java Card da figura 4.20 foi corretamente verificado utilizando a ferramenta Atelier B. Nesse caso, o arquivo de obrigação de prova para o refinamento foi alterado de forma manual, de modo a acomodar o formato das obrigações de prova do retrenchment. 4.8 Avaliação do Método e Perspectivas Futuras Inicialmente, cumpre destacar que, na tese, o método BSmart foi verificado através do esquema genérico de desenvolvimento apresentado neste capítulo. Ele ainda foi aprimorado em dois aspectos, a saber, a proposição do refinamento para verificar a mudança de interface entre os modelos Java e Java Card e a separação das implementações dos serviços da aplicação cartão e do modelo da classe applet. Em suas versões anteriores, o método havia sido avaliado a partir de pequenas especificações, que por vezes não refletiam as necessidades e características de um desenvolvimento smart card típico. Neste trabalho, o desenvolvimento do estudo de caso do passaporte eletrônico (capítulo 7), uma aplicação Java Card “real” que utiliza diversas classes da API, foi de suma importância para analisar e validar o processo de desenvolvimento com o método. A partir dele, tomou-se a resolução de separar os modelos do applet e dos serviços, testou-se e aprimorou-se modelos do KitSmart e foram testadas e corrigidas as ferramentas de suporte ao 84 método. Pretende-se ampliar o método através do modelo do estado global do sistema, desde a codificação e envio dos parâmetros no cliente, a recepção no cartão, e, por fim, o envio da resposta do cartão e a decodificação no cliente. Ou seja, o que se pretende é especificar o ambiente de execução Java Card em uma perspectiva que fornece a verificação ampla do desenvolvimento. Essa parte do método pode ser especificada utilizando-se o formalismo Event-B, por ser mais adequado do que o B clássico para a especificação de sistemas. Cumpre observar que ainda devem ser aprimoradas a abordagem para tratamento de exceções e o modelo do controle de transações. No primeiro caso, a especificação Exception e do KitSmart é utilizada para indicar o lançamento de uma exceção. No trecho de código correspondente a ser gerado em Java Card, o método de lançamento de exceção é invocado caso aconteça algum comportamento de exceção que pode ser previsto. Nesse caso, demarca-se o lançamento de exceção em B. No entanto, ainda não é possível demarcar um trecho de código sob exceção e verificar, através de obrigações de prova, os comportamentos que possam ser aplicados para o casos em que a exceção pode ocorrer. O trabalho de Burdy e Requet [BR03] propõe uma extensão ao método B incluindo cláusulas para modelar o comportamento de exceção da forma como é tratado em linguagens tais como Java ou C++. Esse trabalho, discutido na seção de trabalhos relacionados, é um ponto de partida interessante a ser utilizado para aprimorar o mecanismo de exceção no método BSmart. O controle de transações é outro tópico importante para o desenvolvimento Java Card, visto que deve-se evitar que o estado da aplicação torne-se inconsistente caso a comunicação seja interrompida de forma inesperada. Atualmente, as transações são tratadas de forma semelhante à abordagem para exceções, com o uso de operações da especificação JCSystem para delimitar o começo e o fim de um trecho de código sob transação. No entanto, além de analisar se esta é a melhor forma de lidar com transações, é preciso uma maior análise sobre em que circunstâncias ela deve ser aplicada, uma vez que colocar um código sob o controle de transação é algo custoso para os recursos limitados do hardware smart card. 85 5 Especificação da Tradução para Java Card O presente capítulo apresenta a tradução do módulo de implementação B para a linguagem Java Card. Para tanto, utilizou-se o formalismo para descrição, análise e transformação de linguagens ASF+SDF [BKV08]. De posse da descrição em Syntax Definition Formalism (SDF) das gramáticas da implementação B e da linguagem Java, especificou-se, por meio de um conjunto de regras de reescrita em ASF, as transformações das produções na gramática em B para as produções equivalentes na linguagem Java Card. Observa-se que o conjunto de construções da implementação B que pode ser traduzido para alguma linguagem (neste trabalho, Java Card) é denominado de B0 (B-zero). A gramática da linguagem B é bastante extensa, ainda que se visualize apenas a linguagem B0. Nesse caso, a especificação ASF+SDF foi um importante apoio ao desenvolvimento do gerador de código, atuando como um guia para a implementação, estabelecendo quais construções deveriam ser traduzidas e auxiliando na compreensão de qual a melhor forma de realizar essa tradução. Cumpre ressaltar outra importante motivação para este ramo do trabalho, a saber, a documentação da gramática B utilizada na implementação, bem como da tradução de cada elemento de B para o seu correspondente em Java Card. Assim, a especificação ASF+SDF é um documento conciso que descreve todo o processo de tradução, podendo ser consultado por aqueles que forem utilizar ou estender a ferramenta geradora de código. É importante enfatizar que o conjunto de regras em ASF+SDF não se constitui em um modelo formal da tradução que possa ser provado correto. Nesse caso, seria necessário a descrição da semântica formal de ambas as linguagens e da transformação entre elas. A construção desse modelo semântico não seria possível, dentro do tempo disponível para a conclusão desta tese, sem prejuízo ao cumprimento dos seus demais objetivos. O ambiente ASF+SDF Meta-Environment [ME09] foi a ferramenta utilizada neste ramo do trabalho. O Meta-Environment possui diversas funcionalidades, dentre elas, a verificação 86 sintática da especificação, a geração da árvore de parser a partir das gramáticas, a navegação através da árvore e a execução da transformação do programa na linguagem B0 para o programa equivalente na linguagem Java, a partir da sua árvore sintática e das regras de reescrita do arquivo ASF. Salienta-se que, para o propósito da tese, outras abordagens semelhantes baseadas em casamento de padrões e reescrita de termos poderiam ter sido utilizadas. Por exemplo, é possível citar a linguagem TOM [BBK+ 07], que estende a linguagem Java com construções para casamento de padrões, tipos de dados algébricos e reescrita, e a linguagem para meta-programação Rascal [KvdSV09], desenvolvida pelo mesmo grupo de pesquisa do ASF+SDF. No entanto, pesou na escolha de ASF+SDF o fato de alguns componentes do grupo de pesquisa do autor desta tese já possuírem experiência prévia nesta notação. Nesse sentido, observa-se que a gramática da implementação B utilizada neste trabalho foi inicialmente desenvolvida por Déharbe. Apenas algumas intervenções tiveram que ser feitas, como a adição de produções, pequenas correções em produções existentes, e modificações visando a eliminação de ambiguidades. Este capítulo é dividido conforme descrito a seguir. As próximas duas seções apresentam a fundamentação desta parte do trabalho, a saber, a descrição de linguagens com SDF (seção 5.1) e a especificação de regras de reescrita na linguagem ASF (seção 5.2). O processo completo de especificação da tradução é detalhado na seção 5.3. Por fim, as considerações finais são tecidas na seção 5.4. 5.1 Definição Sintática de Linguagens com SDF O Syntax Definition Formalism (SDF) [BKV07] é uma meta-linguagem que permite a descrição sintática de linguagens na forma de gramáticas livres de contexto e a geração de um parser a partir dessa descrição. O parser de um programa na linguagem resulta em uma árvore de parser, tornando possível a sua manipulação e análise por outras ferramentas. No formalismo SDF, a linguagem é descrita de forma modular, sendo cada módulo responsável pela definição de parte da gramática. Esse aspecto facilita a tarefa da construção da gramática e proporciona o reuso de definições em outros módulos, como se verá mais adiante neste capítulo. Neste trabalho descreveu-se em SDF a gramática da linguagem de implementação B0 necessária ao gerador de código de B para Java Card, tendo como ponto de partida uma descrição da gramática B completa (máquina, refinamento e implementação). Na tese, essa gramática foi restrita apenas para a notação B0, ajustando o escopo para o essencial ao trabalho e facilitando a 87 tarefa de especificação. Por sua vez, a descrição em SDF da linguagem Java, alvo da tradução, foi obtida da biblioteca da ferramenta Meta-Environment. A figura 5.1 exibe os módulos componentes da especificação SDF das linguagens B0 e Java. O componente B02Java, no lado direito e ao centro da figura, relaciona as gramáticas e declara a sintaxe das produções que definem as traduções de B para Java Card. As caixas acima de B02Java representam os módulos da gramática B0. Por sua vez, nas caixas abaixo, são apresentados os módulos da descrição para Java. Os elementos comuns a ambas as linguagens são os módulos Identifier e Integer, os quais definem, respectivamente, a representação de elementos léxicos terminais para identificadores e números inteiros. A unificação desses elementos foi feita com o objetivo de simplificar o trabalho de tradução. Figura 5.1: Componentes das gramáticas B0 e Java Nesta seção apresentam-se os componentes básicos de um módulo SDF, enfatizando os aspectos relevantes à compreensão da especificação descrita na seção 5.3. Uma descrição mais detalhada da linguagem pode ser consultada na documentação oficial do SDF [BKV07]. Um módulo SDF é estruturado conforme o modelo da figura 5.2. O nome do módulo deve ser inserido após a palavra-chave module. Na seção imports devem ser inseridos os identificadores de outros módulos dos quais a definição atual depende. Na figura, o símbolo + se refere a uma ou mais ocorrências. As seções que definem a gramática podem ser introduzidas em exports ou hiddens. No primeiro caso, os elementos da gramática podem ser acessados caso 88 sejam importados por outro módulo. De outro modo, as definições inseridas em hiddens podem ser vistas apenas dentro do módulo em que são declaradas. module imports exports hiddens <module_name > <module_name >+ < g r a m m a r _ e l e m e n t >+ < g r a m m a r _ e l e m e n t >+ Figura 5.2: Elementos básicos de uma definição SDF As seções a seguir detalham os elementos básicos na definição da gramática. Inicialmente, introduz-se os símbolos básicos para a definição dos terminais e não-terminais. Posteriormente, detalham-se as seções da gramática que contém a declaração desses símbolos, a especificação de produções e alguns recursos utilizados para eliminação de ambiguidades. 5.1.1 Símbolos Símbolos são os elementos básicos da gramática. SDF admite três classes de símbolos, a saber, literal, sort e classes de caracteres. Os literais são utilizados na representação de símbolos léxicos de tamanho fixo, compostos apenas por letras, correspondendo ao que seriam os elementos terminais da gramática. Eles devem ser colocados entre aspas, dentro da seção lexical syntax do módulo, como no exemplo abaixo: lexical syntax “true” -> TRUE “false” -> FALSE Neste exemplo, definem-se duas produções simples para representar os terminais booleanos true e false. O texto após o símbolo “->” corresponde ao identificador da produção. Observa-se que as aspas duplas tornam os literais sensíveis à caixa. Se for necessário especificar que os literais podem ser formados por letras maiúsculas e minúsculas, deve-se inseri-los entre aspas simples. Os não-terminais da gramática são declarados como sorts. O identificador de um sort deve iniciar com a primeira letra em maiúscula, seguindo-se combinações de letras, números ou hifens. Por fim, as classes de caracteres são sequências de um ou mais caracteres inseridos entre colchetes. Por exemplo [0 − 9], para representar um número de 0 a 9 ou [A − Za − z] para representar uma letra maiúscula seguida de uma letra minúscula. A tabela 5.1 destaca símbolos que podem ser utilizados na definição da gramática para representar partes opcionais, sequências, repetições e alternativas. 89 Símbolo () ? * + | Significado grupo de símbolos opcional (0 ou 1 ocorrência) 0 ou várias ocorrências 1 ou várias ocorrências alternativa (ou) Exemplo (Predicate “&” Predicate) IfBranch ElsifBranch* ElseBranch? End -> InstructionIf ElsifBranch* “IF” Condition “THEN” { Instruction “;” }+ -> IfBranch “true” | “false”-> Bool Tabela 5.1: Símbolos especiais. 5.1.2 Seções da gramática Os módulos Lexical e Instruction da descrição B0 são utilizados para ilustrar as principais seções e elementos de notação de uma gramática SDF. No módulo Lexical (fig. 5.3) são inseridas as definições para as estruturas elementares da linguagem. Inicialmente, na seção imports, são referenciados os módulos Identifier, com a definição para os símbolos terminais identificadores e Integer, com a definição para os terminais inteiros. Esses elementos foram separados em módulos distintos para que pudessem ser compartilhados como a descrição gramatical para Java. Essa abordagem tornou possível a tradução desses elementos diretamente de uma linguagem para outra, sem a necessidade de regras de reescrita intermediárias. Uma vez que os caracteres permitidos aos identificadores em B são um subconjunto daqueles para Java, é possível, no processo de tradução, restringir os identificadores de Java apenas aos permitidos em B. A priori, para SDF, os terminais da linguagem são simplesmente classes de caracteres, ou seja, sequências de letras ou números. Por outro lado, é possível criar produções para identificar cada token específico, o que é feito na seção lexical syntax do módulo. Na figura 5.3, a produção B-ASTRING define os delimitadores de strings em B. Por sua vez, o símbolo especial LAYOUT corresponde a elementos que devem ser ignorados pelo parser, como comentários, ou aqueles que são inseridos entre os tokens atuando como separadores de símbolos, tais como caracteres de espaço ou de nova linha. Ainda em lexical syntax, vê-se as definições para elementos léxicos da linguagem B, tais como constantes booleanas (TRUE e FALSE) e conjuntos. As produções da gramática podem ser encontradas nas diversas seções de definição de elementos, além da lexical syntax, tem-se a context-free syntax e seções de declaração de variáveis. Uma produção tem a forma: <símbolo>+ -> <símbolo> [{atributos+}]?, ou seja, uma lista de símbolos à esquerda, seguida do operador -> e um símbolo à direita seguido de uma lista opcional de atributos entre chaves. Diz-se que o símbolo à direita é definido pelos símbolos à esquerda. Por exemplo, na produção “INT” -> SetIntegerB0, o conjunto de caracteres “INT” é um token significativo para a linguagem B0, definindo um tipo de conjunto inteiro 90 module B−N o t a t i o n / L e x i c a l i m p o r t s Common / I d e n t i f i e r i m p o r t s Common / I n t e g e r h i d d e n s s o r t s A s t e r i s k B−IDENTIFIER exports s o r t s B−ASTRING B o o l e a n L i t I n t e g e r L i t S e t I n t e g e r B 0 SetEmpty S e t I n t e g e r L i s t I d e n t ListIdentComma S e l e c t e d I d e n t L i s t S e l e c t e d I d e n t l e x i c a l syntax [ \ " ] ~ [ \ " ] ∗ [ \ " ] −> B−ASTRING [ \ \ t \ r \ n ] −> LAYOUT [ \ ∗ ] −> A s t e r i s k " / ∗ " ( A s t e r i s k | ~ [ \ ∗ ] ) ∗ " ∗ / " −> LAYOUT "FALSE" −> B o o l e a n L i t "TRUE" −> B o o l e a n L i t I n t e g e r L i t e r a l −> I n t e g e r L i t "MAXINT" −> I n t e g e r L i t "MININT" −> I n t e g e r L i t "NAT" −> S e t I n t e g e r B 0 "NAT1" −> S e t I n t e g e r B 0 " INT " −> S e t I n t e g e r B 0 "BOOL" −> S e t I n t e g e r B 0 " {} " −> SetEmpty " INT " −> S e t I n t e g e r "INTEGER" −> S e t I n t e g e r "NAT" −> S e t I n t e g e r "NAT1" −> S e t I n t e g e r "STRING" −> S e t I n t e g e r I n t e g e r L i t e r a l −> I d e n t i f i e r { r e j e c t } IntegerLit −> I d e n t i f i e r { r e j e c t } SetInteger −> I d e n t i f i e r { r e j e c t } SetIntegerB0 −> I d e n t i f i e r { r e j e c t } SetEmpty −> I d e n t i f i e r { r e j e c t } BooleanLit −> I d e n t i f i e r { r e j e c t } c o n t e x t −f r e e s y n t a x I d e n t i f i e r −> L i s t I d e n t " ( " { I d e n t i f i e r " , " }+ " ) " −> L i s t I d e n t { I d e n t i f i e r " , " }+ −> L i s t I d e n t C o m m a { I d e n t i f i e r " . " }+ −> S e l e c t e d I d e n t { a v o i d } { S e l e c t e d I d e n t " , " }+ −> L i s t S e l e c t e d I d e n t lexical restrictions A s t e r i s k −/− [ \ / ] " < " −/− [ \ − ] "<−" −/− [ \ − ] LAYOUT? −/− [ \ \ t \ r \ n ] LAYOUT? −/− [ \ / ] . [ \ ∗ ] Figura 5.3: Módulo Lexical da gramática B0 em SDF. (SetIntegerB0). Os tokens “NAT” e “NAT1” também são conjuntos inteiros. Na seção context-free syntax são declarados os elementos não-terminais da linguagem sob a forma de produções mais elaboradas que aquelas encontradas na seção lexical syntax. Por exemplo, uma lista de identificadores (ListIdent) é definida como sendo 1 (um) identificador, ou 1 (um) ou mais identificadores separados por vírgula e entre parênteses. Em lexical restrictions aplicam-se restrições para eliminar ambiguidades no reconhecimento de símbolos da gramática. Por exemplo, em LAYOUT? -/- [\ \t \r \n], tem-se que, após o reconhecimento de um símbolo de LAYOUT, não deve seguir-se o reconhecimento de um outro símbolo de separação (\ \t \r \n). O operador “-/-” deve ser lido como “não deve ser seguido por ”. Ainda em relação à eliminação de ambiguidades, é importante ressaltar o papel dos atribu- 91 tos reject e avoid no módulo Lexical. O primeiro é um mecanismo para filtrar certas derivações de um dado símbolo, removendo essas derivações da árvore de parser. Por exemplo, em SetIntegerB0 -> Identifier {reject}, rejeita-se o reconhecimento, como identificadores, de uma cadeia de caracteres correspondendo aos conjuntos inteiros B0. Por sua vez, o atributo avoid, juntamente com o atributo prefer, também caracterizam-se como filtros sobre alguma produção. Nesses casos, se a partir de um nó da árvore for necessário fazer uma escolha entre mais de uma derivação, aquela com prefer será a sub-árvore escolhida. Por outro lado, as derivações demarcadas com avoid serão preteridas em relação a outras que não tenham avoid. Ressalta-se que o módulo Instruction (figura 5.4) contém as produções para as substituições permitidas ao módulo B de implementação, denominadas de instruções. Utiliza-se esse módulo para exemplificar a definição de não-terminais mais elaborados que aqueles encontrados no módulo Lexical. Em sua maioria, as instruções de mais alto nível (InstructionLevel1) podem referenciar uma ou mais instruções internamente na parte THEN da substituição. Instruções mais complexas, como a condicional, podem ser quebradas em mais de uma produção. O caso geral do IF é a produção InstructionIf, definida como um ramo if seguido de zero ou mais elsif’s (ElsifBranch) e opcionalmente por um else (ElseBranch). 5.2 ASF Enquanto SDF define o aspecto sintático da linguagem, o Abstract Specification Formalism (ASF) ocupa-se do componente semântico, descrito através de regras de reescrita de termos, conforme detalhado na seção 5.2.1. O formalismo denomina-se ASF+SDF devido à estreita ligação entre esses dois componentes, sendo uma especificação ASF composta da sintaxe em SDF estendida com a noção de variáveis e um conjunto de regras de reescrita. Em verdade, os nós da árvore de parser gerada a partir do SDF são os termos em ASF [Vin05], e produções em SDF são utilizadas como funções no sistema de reescrita de ASF. O resultado da aplicação dessas funções e das regras de reescrita sobre a árvore de parser original é uma nova árvore, que pode ser manipulada para diversas aplicações, tais como verificação de tipos, análise de código, refatoração, engenharia reversa e transformação de código. O presente trabalho usa o formalismo ASF+SDF para especificar a transformação de código entre as linguagens B e Java (Java Card). O conjunto de regras de reescrita foi construído de maneira top-down, estabelecendo a tradução de cada elemento da implementação B em seu correspondente na linguagem Java Card. Na seção 5.2.1 introduz-se a reescrita de termos da forma como implementada em ASF. 92 module B−N o t a t i o n / I n s t r u c t i o n i m p o r t s B−N o t a t i o n / L e x i c a l B−N o t a t i o n / C o n d i t i o n B−N o t a t i o n / Term B−N o t a t i o n / C o n d i t i o n B−N o t a t i o n / P r e d i c a t e exports sorts I n s t r u c t i o n I n s t r u c t i o n L e v e l 1 I d e n t i t y ( . . . ) I n s t r u c t i o n B l o c k While c o n t e x t −f r e e s y n t a x I n s t r u c t i o n L e v e l 1 −> I n s t r u c t i o n { prefer } I n s t r u c t i o n B l o c k −> I n s t r u c t i o n L e v e l 1 ( . . . ) While −> I n s t r u c t i o n L e v e l 1 "BEGIN" { I n s t r u c t i o n " ; " }+ "END" −> I n s t r u c t i o n B l o c k "VAR" L i s t I d e n t " IN " { I n s t r u c t i o n " ; " }+ "END" −> I n s t r u c t i o n V a r " skip " −> I d e n t i t y S e l e c t e d I d e n t ( " ( " ListTermComma " ) " ) ? " : = " Term S e l e c t e d I d e n t " := " ExprTable S e l e c t e d I d e n t " : = " "BOOL" " ( " C o n d i t i o n " ) " −> I n s t r u c t i o n B e c o m e s E q u a l −> I n s t r u c t i o n B e c o m e s E q u a l −> I n s t r u c t i o n B e c o m e s E q u a l ( L i s t S e l e c t e d I d e n t "<−−" ) ? S e l e c t e d I d e n t ( " ( " ListTermComma " ) " ) L i s t S e l e c t e d I d e n t "<−−" S e l e c t e d I d e n t −> I n s t r u c t i o n C a l l u p −> I n s t r u c t i o n C a l l u p I f B r a n c h E l s i f B r a n c h ∗ E l s e B r a n c h ? End " I F " C o n d i t i o n "THEN" { I n s t r u c t i o n " ; " }+ " ELSIF " C o n d i t i o n "THEN" { I n s t r u c t i o n " ; " }+ "ELSE" { I n s t r u c t i o n " ; " }+ "END" −> −> −> −> −> InstructionIf IfBranch ElsifBranch ElseBranch End C a s e B r a n c h E i t h e r B r a n c h OrBranch ∗ E l s e B r a n c h End End "CASE" TermSimple "OF" " EITHER " ListTermSimpleComma "THEN" { I n s t r u c t i o n " ; " }+ "OR" ListTermSimpleComma "THEN" { I n s t r u c t i o n " ; " }+ −> −> −> −> InstructionCase CaseBranch EitherBranch OrBranch "WHILE" C o n d i t i o n "DO" { I n s t r u c t i o n " ; " }+ "INVARIANT" C o n d i t i o n "VARIANT" Term "END" −> While Figura 5.4: Módulo Instruction da gramática B0 em SDF. Posteriormente, na seção 5.2.2, apresenta-se a estrutura da descrição em ASF e seus principais componentes. 5.2.1 Reescrita de termos na linguagem ASF Nesta seção introduz-se uma noção básica de reescrita de termos, suficiente para a compreensão do seu uso pelo formalismo ASF. Uma visão mais detalhada sobre o assunto pode ser encontrada em Terese [Ter03]. Inicialmente, cumpre destacar as noções básicas de termo e reescrita. Um termo (T) é uma variável (v), uma constante (c) ou uma aplicação de função (f ) sobre termos [Vin05]. Uma regra de reescrita é um par de termos (T1 → T2 ), sendo o termo T2 o resultado da reescrita de T1 . O processo de reescrita tem início a partir de um termo inicial TI e um conjunto de regras de reescrita R. O objetivo é reduzir (ou reescrever) TI através da aplicação sistemática das regras em R sobre sub-termos de TI , até que o termo inicial seja reduzido a um termo final TF que não 93 pode mais ser simplificado. Nesse estado, diz-se que TF é a forma normal de TI . É possível descrever o processo de reescrita através do conjunto de passos a seguir: 1. Inicialmente, deve-se encontrar um sub-termo de TI que possa ser reduzido, denominado de redex. O redex é obtido quando há um casamento (match) do sub-termo com o lado esquerdo (antecedente) de alguma regra de reescrita (T1 → T2 ). Diz-se que existe um casamento entre dois termos sempre que for possível encontrar uma substituição que torne-os iguais [Kli07]. Por sua vez, uma substituição é o resultado da ligação (ou mapeamento) entre uma variável (do lado esquerdo da regra) e um termo (presente no redex). 2. Caso um redex seja obtido, as variáveis em T2 são substituídas pelos valores encontrados no casamento do passo 1, resultando no termo T2 ’. 3. O redex em TI é então substituído pelo termo T2 ’, e o passo 1 é executado novamente em busca de um novo redex. O processo se repete até que nenhuma redução seja possível. Em ASF as regras de reescrita são descritas através de equations. As equations podem estender a reescrita com a aplicação de regras condicionais. Assim, caso no passo 2, o casamento tenha ocorrido com uma regra precedida de premissas, o passo 3 será efetivado apenas se as premissas forem atendidas. É importante observar que o redex em ASF é selecionado utilizando-se a abordagem left innermost, ou seja, das folhas para o topo da árvore e da esquerda para a direita. No caso das equações, elas podem ser selecionadas em qualquer ordem. 5.2.2 Componentes de uma especificação ASF O componente sintático de uma especificação em ASF compreende o(s) módulo(s) SDF de uma ou mais linguagens e um ou mais módulos SDF contendo definições para os termos referenciados nas equações de reescrita, tais como funções e variáveis. Esses componentes devem estar contidos em arquivos com a extensão .sdf. Cada um desses módulos pode ser estendido com as regras de reescrita, que devem ser inseridas na seção equations de um arquivo com a extensão .asf. Observa-se que a definição sintática de uma função no arquivo SDF é uma produção da seguinte forma: <nome_função> (<Prodori >) -> <Proddest >. No caso da função ser utilizada na transformação entre linguagens, como é o caso nesta tese, Prodori representa uma produção na linguagem de origem e Proddest a sua correspondente na linguagem de destino. Por sua vez, 94 uma variável é associada a alguma produção nessas linguagens (sob a forma: <nome_variável> -> <Prod>), recebendo o valor de um termo no casamento de padrões durante o processo de reescrita, conforme explanado na seção 5.2.1. A seção equations de um arquivo ASF contém a semântica para as definições de função inseridas no arquivo SDF [BKV08]. A semântica é determinada por regras de reescrita correspondentes para cada padrão identificado no lado esquerdo das definições de função. Ressalta-se que as equações podem ser incondicionais ou condicionais. O primeiro tipo, mais simples, tem a forma [< nome >]T1 = T2 . Nesse caso, se o casamento com o T1 for bem sucedido, então o redex é substituído por T2 no termo a ser reduzido. Cada equação deve ser prefixada com um nome, entre colchetes, que a identifique unicamente. Observa-se que, nas equações condicionais, antes que a reescrita possa ocorrer, devem ser verificadas de forma bem-sucedida uma ou mais condições, que podem ser de três tipos, a saber: 1. T1 := T2 : condição é verdadeira quando ao reduzir T2 é possível o casamento com T1 , situação em que as variáveis em T1 recebem novos valores; 2. T1 ! := T2 : condição é verdadeira quando a redução do termo T2 não casa com o termo T1 ; 3. T1 == T2 : condição é verdadeira quando a redução de T1 e de T2 resulta em dois termos sintaticamente idênticos. As equações condicionais podem ser descritas de três formas, sintaticamente distintas, mas com o mesmo significado. Abaixo, <C1 >, <C2 >, . . . , <CN > representam uma ou mais condições. 1. [< nome >] T1 = T2 when <C1 >, <C2 >, . . . , <CN > 2. [< nome >] <C1 >, <C2 >, . . . , <CN > ===> T1 = T2 3. [< nome >] <C1 >, <C2 >, . . . , <CN > ============= T1 = T2 5.3 Traduzindo de B para Java Card Nesta seção detalham-se as regras de reescrita que regem a tradução da implementação B0 para Java Card. Adotou-se o modelo top-down na construção das regras, partido dos termos de 95 mais alto-nível no topo da árvore de parser, a saber, as cláusulas da implementação e descendo em direção aos elementos internos de cada cláusula. Destaca-se, inicialmente, na Seção 5.3.1, a parte sintática (SDF) da tradução. Nela são estabelecidas as assinaturas das funções de tradução que reduzem um termo na árvore B para o termo correspondente na árvore Java. As implementações dessas funções são parte das equações (equations) de reescrita inseridas no arquivo ASF correspondente. Além disso, no arquivo SDF, também são definidas as variáveis utilizadas em cada regra. Em relação ao componente ASF, na Seção 5.3.2, são delineadas as equações de mais alto nível. Nas demais seções são apresentadas as regras de tradução para cada construção da implementação B. É pertinente observar que, devido ao ASF não ter suporte nativo para inferência de tipos, se escolheu short como o tipo padrão nas regras que referenciam elementos cujo tipo em B pode ser obtido somente por inferência, como o tipo do retorno e dos parâmetros de uma operação. Na tradução em si, através da API BCompiler [Cle12b] é possível obter os tipos corretos dessas construções. Cumpre ressaltar que os arquivos ASF+SDF, que especificam a tradução, podem ser consultados no Apêndice B. A gramática SDF da linguagem Java pode ser encontrada na biblioteca da ferramenta de desenvolvimento ASF+SDF Meta-environment [ME09]. 5.3.1 Parte SDF da tradução O componente sintático da especificação da tradução é inserido no arquivo SDF B02Java, exibido na figura 5.6. Inicialmente, são referenciados os módulos SDF da linguagem de implementação B0 (Clause) e da linguagem Java (Java) que contém os símbolos iniciais de ambas as gramáticas (fig. 5.5), estabelecidos na seção context-free start-symbols. No caso de B, o símbolo inicial é a produção Implementation. Por sua vez, CompilationUnit é o símbolo de mais alto nível da gramática Java. "IMPLEMENTATION" H e a d e r C l a u s e R e f i n e s I m p l e m e n t a t i o n C l a u s e ∗ "END" −> I m p l e m e n t a t i o n P a c k a g e D e c l a r a t i o n ? I m p o r t D e c l a r a t i o n ∗ T y p e D e c l a r a t i o n + −> C o m p i l a t i o n U n i t Figura 5.5: Símbolos iniciais das gramáticas B e Java. Observa-se que, na seção context-free syntax da figura 5.6, são descritas sintaticamente as funções utilizadas nas regras de reescrita para realizar a tradução. A função de mais alto nível é trd-bcomp, que recebe como entrada a árvore B contendo o cabeçalho da implementação e um 96 conjunto de 0 (zero) ou mais cláusulas e as reduz a uma árvore Java correspondente, a partir da qual é gerado o código Java Card. As demais funções, detalhadas posteriormente neste capítulo, relacionam-se à reescrita de cláusulas, instruções, termos, condições, tipos e demais elementos de uma implementação B. Por fim, são definidas, na seção variables, variáveis para representar os não-terminais referenciados nas diversas regras. Conforme descrito anteriormente na seção 5.2.1, as variáveis são utilizadas no momento do casamento de padrões entre o lado esquerdo da equação de reescrita e o termo de entrada, sendo ligadas à produção correspondente nesse termo. Quando necessário à tradução, as variáveis são reduzidas ao termo equivalente em Java. Nesse caso, variáveis representando não-terminais Java são utilizadas para receber o resultado da redução. Observa-se que todas as variáveis foram prefixadas com um $. Além disso, as variáveis que representam listas recebem o sufixo ∗, quando tratar-se de zero ou mais repetições, e o sufixo +, para uma ou mais repetições. module B−N o t a t i o n / B02Java i m p o r t s B−N o t a t i o n / C l a u s e java / syntax / Java exports c o n t e x t −f r e e s t a r t −s y m b o l s Implementation CompilationUnit c o n t e x t −f r e e s y n t a x t r d −bcomp ( I m p l e m e n t a t i o n ) −> C o m p i l a t i o n U n i t t r d −c l a u s e s ( I m p l e m e n t a t i o n C l a u s e ∗ ) −> C l a s s B o d y D e c l a r a t i o n ∗ t r d −c l a u s e ( I m p l e m e n t a t i o n C l a u s e + ) −> C l a s s B o d y D e c l a r a t i o n + t r d −i m p o r t s ( { I m p o r t " , " } + ) −> C l a s s B o d y D e c l a r a t i o n + t r d −s e e s ( { S e l e c t e d I d e n t " , " } + ) −> C l a s s B o d y D e c l a r a t i o n + t r d −p r o m o t e s ( { S e l e c t e d I d e n t " , " } + ) −> C l a s s B o d y D e c l a r a t i o n + t r d −s e t s ( { S e t " ; " } + ) −> C l a s s B o d y D e c l a r a t i o n + t r d −v a l u a t i o n ( { V a l u a t i o n " ; " } + ) −> C l a s s B o d y D e c l a r a t i o n + t r d −o p e r a t i o n s ( { O p e r a t i o n B 0 " ; " } + ) −> C l a s s B o d y D e c l a r a t i o n + t r d −i n s t r u c t i o n s ({ I n s t r u c t i o n " ; " }+) −> B l o c k S t a t e m e n t + ( . . . ) t r d −c o n d i t i o n ( C o n d i t i o n ) −> P r i m a r y t r d −t e r m ( Term ) −> P r i m a r y t r d − l i s t −term −comma ( { Term " , " } + ) −> { P r i m a r y " , " }+ t r d − l i s t i d −t o −s t a t i c −c o n s t ( L i s t I d e n t C o m m a ) −> C l a s s B o d y D e c l a r a t i o n + t r d − l i s t i d −t o −param− l i s t ( { I d e n t i f i e r " , " } + ) −> { F o r m a l P a r a m e t e r " , " }+ t r d − l i s t i d −t o −v a r −d e c l ( { I d e n t i f i e r " , " } + ) −> B l o c k S t a t e m e n t + t r d −p r e d −t o −v a r −t y p e ( P r e d i c a t e ) −> C l a s s B o d y D e c l a r a t i o n + t r d −p r e d −t o −c o n s t −t y p e ( P r e d i c a t e ) −> C l a s s B o d y D e c l a r a t i o n + hiddens variables " $BImpl " [0 −9]∗ −> I m p l e m e n t a t i o n " $ B I n s t " [0 −9]∗ −> I n s t r u c t i o n " $BInsts1 +" −> { I n s t r u c t i o n " ; " }+ " $BTerm " [0 −9]∗ " −> Term " $ J P r E x p r " [0 −9]∗ −> P r i m a r y " $JPrExpr1+" −> { P r i m a r y " , " }+ (...) Figura 5.6: Parte SDF especificação da tradução. 5.3.2 Regras de reescrita globais Cumpre destacar que toda árvore de parser possui, implicitamente, uma produção start que faz referência ao seu topo. Para ser capaz de iniciar o processo de tradução, deve-se inicialmente 97 realizar o casamento entre essas produções das gramáticas B0 e Java, através das suas funções start, na regra nomeada como main na figura 5.7. A função start para a árvore B, no lado esquerdo da regra, recebe a produção inicial Implementation e uma variável $BImpl que irá receber a raiz da árvore sintática. Por sua vez, no lado direito de main, a função start para Java recebe o termo inicial CompilationUnit. O parâmetro relacionado à árvore da gramática Java irá resultar da reescrita de $BImpl por meio da função trd-bcomp. A regra trd-component (fig. 5.7) usa a função trd-bcomp para iniciar efetivamente a tradução a partir dos termos iniciais da gramática. No lado esquerdo da regra (após o ===> e antes do =) é estabelecido o padrão para uma implementação B válida, ou seja, o terminal IMPLEMENTATION seguido do nome da máquina ($BId0), da cláusula refines (variável $Refines), de 0 (zero) ou mais cláusulas ($ImpClause*0) e do terminal END. [ main ] s t a r t ( I m p l e m e n t a t i o n , $BImpl ) = s t a r t ( C o m p i l a t i o n U n i t , t r d −bcomp ( $BImpl ) ) [ t r d −component ] $ C l a s s B o d y 1 ∗ : = t r d −c l a u s e s ( $ I m p C l a u s e ∗ 0 ) ===> t r d −bcomp (IMPLEMENTATION $BId0 $ R e f i n e s $ I m p C l a u s e ∗0 END) = p u b l i c c l a s s $BId0 e x t e n d s j a v a x . f r a m e w o r k . A p p l e t { $ClassBody1 ∗ } Figura 5.7: Regras iniciais do processo de tradução Para que a reescrita no lado direito ocorra, ainda deve-se verificar a condição anterior à regra, que no caso estabelece a redução das cláusulas em zero ou mais elementos de corpo de classe (variável ClassBody1*). Um corpo de classe é definido na produção ClassBodyDeclaration e pode ser qualquer construção em Java que possar estar inserida em um bloco de código (entre { } abre-e-fecha chaves), tais como métodos, classes internas e declarações de variáveis. Observa-se que as variáveis $BId0 (um identificador) e $Refines não necessitam ser referenciadas na condição. No primeiro caso, deve-se ao fato de tratar-se da mesma produção, tanto em B quanto em Java, não precisando portanto ser reduzida. De outro modo, o identificador do módulo em $Refines é utilizado apenas no casamento de padrões, não sendo necessária a sua tradução no código Java. Evidencia-se que a reescrita irá resultar na classe Java Card da aplicação cartão. Inicialmente, é inserido o cabeçalho, contendo o mesmo nome do módulo de implementação B, recebido na variável $BId0, seguido da declaração de importação da classe Applet e do início do bloco da classe ({). Em seguida, o corpo da classe é formado a partir da reescrita de cada cláusula B em um elemento equivalente de corpo de classe (variável $ClassBody1*). Observa-se que, antes de se apresentar a tradução de cada cláusula, demonstrar-se-á a re- 98 escrita de predicados e termos, devido a essas construções serem referenciadas em algumas cláusulas. 5.3.3 Predicados Salienta-se que, de todo o conjunto de predicados de B, são traduzidos apenas aqueles relacionados à definição de tipos (fig. 5.8), apresentados nesta seção, e os predicados denominados de condições, descritos na seção 5.3.6. Para a resolução dos predicados de tipagem, são definidas as funções trd-pred-to-var-type e trd-pred-to-const-type. A primeira reescreve o predicado em uma declaração de variável, e a segunda o traduz para uma declaração de constante em Java (uma variável static final). Ressalta-se que foram definidas regras que representam a tradução para os tipos inteiros de Java Card, arrays e sequências (na figura 5.8, apenas a tradução para byte) e objetos. As regras para reescrita de constantes são semelhantes àquelas para variáveis, acrescentando-se apenas o prefixo static final à declaração. Por este motivo, elas foram suprimidas da figura 5.8. [ t r d −p r e d −v a r −t p −p a r e n ] $ C l a s s B o d y 1 ∗ : = t r d −p r e d −t o −v a r −t y p e ( $ P r e d 1 ) ===> t r d −p r e d −t o −v a r −t y p e ( ( $ P r e d 1 ) ) = $ClassBody1 ∗ [ t r d −p r e d −v a r −t p −and ] $ C l a s s B o d y 1 ∗ : = t r d −p r e d −t o −v a r −t y p e ( $ P r e d 1 ) , $ C l a s s B o d y 2 ∗ : = t r d −p r e d −t o −v a r −t y p e ( $ P r e d 2 ) ===> t r d −p r e d −t o −v a r −t y p e ( $ P r e d 1 & $ P r e d 2 ) = $ClassBody1 ∗ $ClassBody2 ∗ [ t r d −p r e d −v a r −t p −o b j e c t ] t r d −p r e d −t o −v a r −t y p e ( $BId0 : $BId1 ) = $BId1 $BId0 ; [ t r d −p r e d −v a r −t p −s h o r t ] $BId1 == JSHORT ===> t r d −p r e d −t o −v a r −t y p e ( $BId0 : $BId1 ) = s h o r t $BId0 ; [ t r d −p r e d −v a r −t p −b o o l ] t r d −p r e d −t o −v a r −t y p e ( $BId0 : BOOL) = b o o l e a n $BId0 ; [ t r d −p r e d −v a r −t p −b o o l −2] $BId1 == JBOOLEAN ===> t r d −p r e d −t o −v a r −t y p e ( $BId0 : $BId1 ) = b o o l e a n $BId0 ; [ t r d −p r e d −v a r −t p −a r r a y −b y t e ] $BId2 == JBYTE ===> t r d −p r e d −t o −v a r −t y p e ( $BId0 : $BId1 −−> $BId2 ) = b y t e [ ] $BId0 ; [ t r d −p r e d −c o n s t −t p −seq−b y t e ] t r d −p r e d −t o −v a r −t y p e ( $BId0 : s e q ( JBYTE ) ) = p u b l i c b y t e [ ] $BId0 ; [ t r d −p r e d −v a r −t p −seq1 −b y t e ] t r d −p r e d −t o −v a r −t y p e ( $BId0 : s e q 1 ( JBYTE ) ) = p u b l i c b y t e [ ] $BId0 ; [ t r d −p r e d −v a r −t p −b y t e ] $BId1 == JBYTE ===> t r d −p r e d −t o −v a r −t y p e ( $BId0 : $BId1 ) = b y t e $BId0 ; Figura 5.8: Regras de reescrita para predicados de tipagem 99 5.3.4 Termos Cumpre destacar que os termos são parte das expressões permitidas para a implementação B, desde as elementares, tais como identificadores, literais inteiros e booleanos, até expressões compostas, como as aritméticas. Observa-se que foram implementados o conjunto de termos destacados nas equações de reescrita das figuras 5.9 a 5.11. Os termos relacionados a estruturas (struct), recurso pertencente à linguagem B da ferramenta Atelier B, ainda não se encontram implementados nesta versão. Os termos básicos são apresentados na figura 5.9. Nela tem-se a definição de que um SelectedIdent ({Identifier "."}+) é um termo, representado pela variável $SelId, sendo traduzido para o mesmo identificador em Java Card. Também é trivial a tradução de um literal inteiro, para o mesmo literal no código Java Card. O conjunto vazio ({}) e a sequência vazia ([]) são traduzidos para a palavra-chave null em Java. As demais expressões básicas correspondem às constantes para os valores máximos e mínimos do tipo inteiro e a tradução de um termo entre parênteses. É importante observar que, na linguagem B0, o tipo inteiro disponível é finito, com faixa de valores correspondente à faixa disponível para o inteiro de Java. [ t r d −bterm −s e l i d −1] t r d −t e r m ( $ S e l I d ) = $ S e l I d [ t r d −bterm −seq−empty ] t r d −t e r m ( [ ] ) = n u l l [ t r d −bterm −b o o l −1] t r d −t e r m (TRUE) = t r u e [ t r d −bterm −i n t r −2] t r d −t e r m (MAXINT) = I n t e g e r .MAX_VALUE [ t r d −bterm −b o o l −2] t r d −t e r m ( FALSE ) = f a l s e [ t r d −bterm −i n t r −1] t r d −t e r m ( $ D e c I n t L i t ) = $DecIntLit [ t r d −bterm −s e t −empty ] t r d −t e r m ( { } ) = n u l l [ t r d −bterm −i n t r −3] t r d −t e r m ( MININT ) = I n t e g e r . MIN_VALUE [ t r d −bterm −p a r e n ] $ J P r E x p r 0 : = t r d −t e r m ( $BTerm0 ) ===> t r d −t e r m ( ( $BTerm0 ) ) = ( $ J P r E x p r 0 ) Figura 5.9: Regras para rescrita de termos básicos A figura 5.10 exibe expressões de chamadas de funções predefinidas em B. A função bool ($Cond0), retorna um valor booleano de acordo com a avaliação da condição que ela recebe. A condição é reescrita para uma expressão em Java ($JPrExpr), sendo essa inserida na parte de teste de um operador condicional. As demais funções são predecessor (pred ($BTerm0)), reescrita em uma expressão subtraída de 1 (um) e sucessor (succ ($BTerm0)), traduzida em uma expressão acrescida de uma unidade. O último conjunto de expressões reescritas diz respeito às expressões aritméticas (fig. 5.11). Esses termos são compostos por um ou dois operandos, ou seja, duas expressões ($BTerm0 e $BTerm1) e um operador. Na condição de cada regra (omitidas da figura, exceto para primeira) os operandos são reescritos nas expressões equivalentes em Java através das variáveis $JPrExpr0 e $JPrExpr1. Por sua vez, os operadores são simplesmente os terminais correspon- 100 [ t r d −bterm −op−r e s −1] $ J P r E x p r : = t r d −c o n d i t i o n ( $BCond0 ) ===> t r d −t e r m ( b o o l ( $BCond0 ) ) = ( $JPrExpr ) ? true : f a l s e [ t r d −bterm −op−r e s −3] $ J P r E x p r 0 : = t r d −t e r m ( $BTerm0 ) ===> t r d −t e r m ( p r e d ( $BTerm0 ) ) = ( ( $JPrExpr0 ) − 1) [ t r d −bterm −op−r e s −2] $ J P r E x p r 0 : = t r d −t e r m ( $BTerm0 ) ===> t r d −t e r m ( s u c c ( $BTerm0 ) ) = ( ( $JPrExpr0 ) + 1) Figura 5.10: Regras para rescrita de termos funcionais dentes em Java. [ t r d −bterm −exp−a r −1] $ J P r E x p r 0 : = t r d −t e r m ( $BTerm0 ) , $ J P r E x p r 1 : = t r d −t e r m ( $BTerm1 ) ===> t r d −t e r m ( $BTerm0 + $BTerm1 ) = $JPrExpr0 + $JPrExpr1 [ t r d −bterm −exp−a r −2] t r d −t e r m ( $BTerm0 − $BTerm1 ) = $JPrExpr0 − $JPrExpr1 [ t r d −bterm −exp−a r −3] t r d −t e r m (−$BTerm0 ) = −( $ J P r E x p r 0 ) [ t r d −bterm −exp−a r −4] t r d −t e r m ( $BTerm0 ∗ $BTerm1 ) = $JPrExpr0 ∗ $JPrExpr1 [ t r d −bterm −exp−a r −5] t r d −t e r m ( $BTerm0 / $BTerm1 ) = $JPrExpr0 / $JPrExpr1 [ t r d −bterm −exp−a r −6] t r d −t e r m ( $BTerm0 mod $BTerm1 ) = $ J P r E x p r 0 \% $ J P r E x p r 1 [ t r d −bterm −exp−a r −7] t r d −t e r m ( $BTerm0 ∗∗ $BTerm1 ) = Math . pow ( $ J P r E x p r 0 , $ J P r E x p r 1 ) Figura 5.11: Regras para rescrita de expressões aritméticas. 5.3.5 Cláusulas As cláusulas são os componentes globais da tradução. Conforme definido anteriormente, uma cláusula dará origem a um bloco de comandos no corpo da classe gerada. No caso da geração da classe applet, a cláusula EXTENDS não encontra-se especificada. Uma vez que o applet já herda de javax.framework.applet, não é possível herdar de outro componente. Para as demais classes Java Card que podem ser traduzidas no cartão, apenas a referência ao primeiro módulo na cláusula EXTENDS é traduzida no cabeçalho da classe. Uma mensagem de erro é enviada ao usuário, e o processo de tradução é interrompido, caso mais de um módulo seja estendido. Observa-se que as cláusulas da implementação que não possuem correspondente em Java, tal qual a cláusula de asserções (ASSERTIONS), são traduzidas em um “;” (ponto-e-vírgula), símbolo Java para terminação de comandos. O ponto-e-vírgula pode ser inserido no corpo da classe sem alterar a sua semântica. As demais cláusulas que se enquadram nesse caso são aquelas relacionadas a nomeação de variáveis e constantes. 101 A regra geral para a tradução de cláusulas é trd-clauses (figura 5.12). Caso não haja nenhuma cláusula, a regra trd-clauses() = ; é aplicada. No caso geral, uma lista de cláusulas casa com a regra nomeada trd-n-clauses. O padrão definido nessa regra é uma cláusula ($ImpClause1) seguida de zero ou mais cláusulas ($ImpClause*). Cada cláusula individual é reduzida em $ClassBody1* através da sua função trd-clause (sem “s”). Há pelo menos uma função trdclause para cada cláusula a ser traduzida, consoante apresentado posteriormente nesta seção. A segunda variável de corpo de classe ($ClassBody2*) recebe a próxima cláusula a ser reduzida através de uma nova chamada à função trd-clauses, em um processo que se repete até que todas as cláusulas sejam traduzidas. [ t r d −0− c l a u s e ] t r d −c l a u s e s ( ) = ; [ t r d −n−c l a u s e s ] $ C l a s s B o d y 1 ∗ : = t r d −c l a u s e ( $ I m p C l a u s e 1 ) , $ C l a s s B o d y 2 ∗ : = t r d −c l a u s e s ( $ I m p C l a u s e ∗ ) ===> t r d −c l a u s e s ( $ I m p C l a u s e 1 $ I m p C l a u s e ∗ ) = $ClassBody1 ∗ $ClassBody2 ∗ Figura 5.12: Regras de reescrita para uma sequência de zero ou mais cláusulas Inclusão de máquinas Apresentam-se na figura 5.13 as regras de tradução que regem as cláusulas que fazem referência a especificações externas, a saber, IMPORTS, SEES e PROMOTES. Apenas as equações que tratam da tradução de uma máquina individualmente são exibidas. No apêndice B encontram-se as demais regras utilizadas para percorrer uma lista de módulos. [ t r d −1−import ] t r d −i m p o r t s ( $BId0 . $BId1 ) = { p u b l i c c l a s s $BId1 { } $BId1 $BId0 = new $BId1 ( ) ; } [ t r d −s e e s −1] t r d −s e e s ( $BId0 . $BId1 ) = p u b l i c c l a s s $BId1 { } [ t r d −s e e s −2] t r d −s e e s ( $BId0 ) = p u b l i c c l a s s $BId0 { } [ t r d −1−p r o m o t e ] t r d −p r o m o t e s ( $BId0 . $BId1 ) = p u b l i c s h o r t $BId1 ( s h o r t p r ) { s h o r t $BId0 = new $BId0 ( p r ) ; r e t u r n $BId0 . $BId1 ( p r ) ; } Figura 5.13: Equações de reescrita para cláusulas de inclusão de máquinas. No caso de uma máquina importada, deve ser gerada uma nova classe para a máquina e, na classe applet, um atributo ($BId0) que referencia um objeto do módulo importado. Na implementação da ferramenta a nova classe é gerada em um arquivo externo e o atributo é instanciado no método INITIALISATION, que é chamado pelo método construtor da classe, 102 assegurando assim a otimização de que a instância é criada apenas uma vez, no momento da primeira inicialização. Observa-se através da antecedente da regra (BId0.BId1) que o nome da máquina importada deve ser obrigatoriamente prefixado com um identificador de instância, de modo a possibilitar que ele seja traduzido no nome do atributo que será criado. Ressalta-se que uma máquina vista (cláusula SEES) deve gerar uma classe correspondente. No entanto, não é preciso criar uma referência a ela no applet, considerando que apenas conjuntos (objetos) e constantes (variáveis final em Java) podem ser acessados. Dessa forma, esses componentes, no momento da tradução, são prefixados com o nome da máquina a qual pertencem. Para finalizar a explanação sobre as máquinas inclusas, destaca-se a tradução da cláusula PROMOTES. O significado dessa cláusula é promover uma ou mais operações de máquinas importadas a operações da máquina que as inclui. Assim, na implementação, a tradução é feita conforme apresentado no lado direito da equação de reescrita. Cada operação promovida é reduzida a um método na classe Java e, no corpo desse método, a classe correspondente à operação B original é instanciada. Através da referência criada, o método original é então chamado. Conjuntos As regras gerais para a reescrita de uma lista de conjuntos são demonstradas na figura 5.14. A regra de partida é trd-clause que utiliza-se de trd-sets para percorrer a lista. Ressalta-se que um conjunto em B define um novo tipo de dados. Tendo em vista manter essa semântica, um conjunto é traduzido em uma classe Java. [ t r d −c l a u s e −s e t s ] $ C l a s s B o d y 1 + : = t r d −s e t s ( $ S e t s 1 + ) ===> t r d −c l a u s e ( SETS $ S e t s 1 + ) = $ClassBody1+ [ t r d −s e t s −n ] $ C l a s s B o d y 1 + : = t r d −s e t s ( $ S e t s 1 + ) , $ClassBody2+ : = t r d −s e t s ( $ S e t 1 ) ===> t r d −s e t s ( $ S e t s 1 + ; $ S e t 1 ) = $ClassBody1+ $ClassBody2+ Figura 5.14: Regras de reescrita para uma lista de conjuntos A figura 5.15 exibe as equações que regem a tradução dos conjuntos enumerados (trd-setenum) e adiados (trd-set-def ). Na regra trd-lid-to-stat-n, através da função trd-listid-to-stat, cada elemento de um conjunto enumerado é traduzido em uma constante byte correspondente no corpo da classe Java Card. Nesse caso, o mais adequado seria a tradução para um tipo enumerado em Java. Entretanto, observa-se que este recurso não existe na versão Java 1.3, a máxima permitida a Java Card 2.2.x. No caso dos conjuntos adiados, a nova classe gerada não 103 recebe implementação. [ t r d −s e t −enum ] $ClassBody1+ := t r d − l i s t i d −t o −s t a t i c −c o n s t ( $ L i s t I d + ) ===> t r d −s e t s ( $BId0 = { $ L i s t I d + } ) = p u b l i c c l a s s $BId0 { $ClassBody1+ } [ t r d −s e t −d e f ] t r d −s e t s ( $BId0 ) = c l a s s $BId0 { [ t r d −l i d −t o −s t a t i c −n ] $ClassBody1+ := t r d − l i s t i d −t o −s t a t i c −c o n s t ( $ L i s t I d + ) , $ClassBody2+ := t r d − l i s t i d −t o −s t a t i c −c o n s t ( $BId ) ===> t r d − l i s t i d −t o −s t a t i c −c o n s t ( $ L i s t I d + , $BId ) = $ClassBody1+ $ClassBody2+ [ t r d −l i d −t o −s t a t i c −1] t r d − l i s t i d −t o −s t a t i c −c o n s t ( $BId0 ) = p u b l i c s t a t i c f i n a l b y t e $BId0 ; } Figura 5.15: Regras para reescrita de conjuntos adiados e enumerados. Constantes e valoração de constantes Tendo em vista a correta tradução de constantes, devem constar na implementação, obrigatoriamente, as cláusulas PROPERTIES, contendo a definição do tipo de cada constantes e VALUES, na qual são definidos seus valores concretos. A cláusula CONSTANTS não é necessária ao processo de reescrita, uma vez que os identificadores das constantes podem ser diretamente obtidos da cláusula PROPERTIES. É importante salientar que as propriedades de tipagem em PROPERTIES são reescritas, através da função trd-pred-to-const-type (seção 5.3.3), em declarações de constantes (variáveis static final) em Java Card. A inicialização de tais constantes no código Java, em blocos static, é resultado da tradução das valorações em VALUES. [ t r d −c l a u s e −p r o p ] $ C l a s s B o d y 1 + : = t r d −p r e d −t o −c o n s t −t y p e ( $ P r e d ) ===> t r d −c l a u s e ( PROPERTIES $ P r e d ) = $ C l a s s B o d y 1 + [ t r d −v a l u a t i o n −2] $ J P r E x p r 0 : = t r d −t e r m ( $BTerm0 ) ===> t r d −v a l u a t i o n ( $BId0 = $BTerm0 ) = static { $BId0 = $ J P r E x p r 0 ; } [ t r d −c l a u s e −v a l u e s ] $ C l a s s B o d y 1 + : = t r d −v a l u a t i o n ( $ V a l s 1 + ) ===> t r d −c l a u s e (VALUES $ V a l s 1 + ) = $ClassBody1+ [ t r d −v a l u a t i o n −3] $ J P r E x p r 0 : = t r d −c o n d i t i o n ( $BCond0 ) ===> t r d −v a l u a t i o n ( $BId0 = BOOL ( $BCond0 ) ) = static { $BId0 = ( ( $ J P r E x p r 0 ) ? t r u e : f a l s e ) ; } Figura 5.16: Regras para tradução de constantes. Invariante e inicialização Os predicados para tipagem de variáveis concretas no invariante são reescritos em declarações de atributos em Java, através da função trd-pred-to-var-type (seção 5.3.3). Este proce- 104 dimento é semelhante ao que é feito para constantes, com a diferença que os atributos gerados não são static final. [ t r d −c l a u s e −i n v ] $ClassBody1+ := t r d −p r e d −t o −v a r −t y p e ( $ P r e d ) ===> t r d −c l a u s e ( INVARIANT $ P r e d ) = $ClassBody1+ [ t r d −c l a u s e − i n i t i a l i s a t i o n ] $JBlockSt1 + := t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d −c l a u s e ( INITIALISATION $ B I n s t s 1 + ) = v o i d INITIALISATION ( ) { $JBlockSt1+ } Figura 5.17: Tradução do invariante e da inicialização Destaca-se que as instruções de inicialização em B são reescritas dentro de um método à parte no código Java Card, denominado de INITIALISATION. De modo a garantir que a inicialização seja feita apenas uma vez durante toda a vida da aplicação, esse método é chamado pelo construtor do applet. Apesar de não estar explícito na equação de reescrita trd-clauseinitialisation, na tradução para Java Card, a inicialização dos atributos gerados para cada máquina importada também é realizada no método INITIALISATION. Operações A regra geral de reescrita para as operações (trd-clause-op) realiza o casamento do termo de entrada, uma lista de operações, e utiliza-se da regra trd-b-oper-n para iterar sobre a lista. Observa-se que a tradução em si é feita apenas na regra trd-b-oper-1, casando o padrão de uma operação em B e traduzindo para um método em Java. As operações declaradas na implementação B dos serviços são todas traduzidas com escopo público (public). Outras regras (não exibidas na figura) tratam das demais variações de declaração de operações. [ t r d −c l a u s e −op ] $ C l a s s B o d y 1 + : = t r d −o p e r a t i o n s ( $ L i s t O p + ) ===> t r d −c l a u s e ( OPERATIONS $ L i s t O p + ) = $ClassBody1+ [ t r d −b−o p e r −n ] $ C l a s s B o d y 1 + : = t r d −o p e r a t i o n s ( $ L i s t O p + ) , $ C l a s s B o d y 2 + : = t r d −o p e r a t i o n s ( $Op1 ) ===> t r d −o p e r a t i o n s ( $ L i s t O p + ; $Op1 ) = $ClassBody1+ $ClassBody2+ [ t r d −b−o p e r −1] $FParams1 + : = t r d − l i s t i d −t o −param− l i s t ( $ListIdComma1 + ) , $JBlockSt1 + := t rd −i n s t r u c t i o n s ( $BInstL ) ===> t r d −o p e r a t i o n s ( $BId0 <−− $BId1 ( $ListIdComma1 + ) = $ B I n s t L ) = p u b l i c s h o r t $BId1 ( $FParams1 + ) { s h o r t $BId0 ; $JBlockSt1 r e t u r n $BId0 ; } Figura 5.18: Parte das regras de rescrita para tradução de operações. Deve-se observar que, antes da efetivação da reescrita, há a redução de dois termos na regra, um deles contendo a lista de parâmetros da operação ($ListIdComma1+) e outro a instrução (substituição em B0) no corpo da operação ($BInstL). O primeiro termo é traduzido com o 105 auxílio da função trd-listid-to-param-list, nas equações de reescrita da figura 5.19, que efetiva a tradução dos parâmetros da operação nos parâmetros equivalentes dos métodos. Por outro lado, o corpo da operação é traduzido por meio da função trd-instructions, utilizada na reescrita de cada uma das possíveis instruções em B em suas equivalentes Java. [ t r d −l i d −param− l i s t −1] t r d − l i s t i d −t o −param− l i s t ( $BId1 ) = s h o r t $BId1 [ t r d −l i d −param− l i s t −n ] $FParams1 + : = t r d − l i s t i d −t o −param− l i s t ( $ListIdComma0 + ) , $FParams2 + : = t r d − l i s t i d −t o −param− l i s t ( $BId0 ) ===> t r d − l i s t i d −t o −param− l i s t ( $ListIdComma0 + , $BId0 ) = $FParams1 + , $FParams2 + Figura 5.19: Regra auxiliar para tradução dos parâmetros das operações 5.3.6 Condições As condições correspondem aos predicados concretos que podem ser utilizados na implementação, além daqueles relacionados à definição de tipos. Cada sub-termo componente da condição ($BTermSimp0) é reduzido a uma expressão em Java ($JPrExpr0). O resultado da reescrita é a composição dessas expressões com o operador equivalente em Java. Na figura 5.20, apenas a condição da primeira equação de reescrita é apresentada. As demais, por serem definidas de forma semelhante, foram omitidas. [ t r d −cond −1] $ J P r E x p r 0 : = t r d −t e r m ( $BTermSimp0 ) , $ J P r E x p r 1 : = t r d −t e r m ( $BTermSimp1 ) ===> t r d −c o n d i t i o n ( $BTermSimp0 = $BTermSimp1 ) = $ J P r E x p r 0 == $ J P r E x p r 1 [ t r d −cond −2] t r d −c o n d i t i o n ( $BTermSimp0 / = $BTermSimp1 ) = $JPrExpr0 != $JPrExpr1 [ t r d −cond −3] t r d −c o n d i t i o n ( $BTermSimp0 < $BTermSimp1 ) = $JPrExpr0 < $JPrExpr1 [ t r d −cond −4] t r d −c o n d i t i o n ( $BTermSimp0 > $BTermSimp1 ) = $JPrExpr0 > $JPrExpr1 [ t r d −cond −6] t r d −c o n d i t i o n ( $BTermSimp0 >= $BTermSimp1 ) = $ J P r E x p r 0 >= $ J P r E x p r 1 [ t r d −cond −7] t r d −c o n d i t i o n ( $BCond0 & $BCond1 ) = $ J P r E x p r 0 && $ J P r E x p r 1 [ t r d −cond −8] t r d −c o n d i t i o n ( $BCond0 o r $BCond1 ) = $JPrExpr0 | | $JPrExpr1 [ t r d −cond −9] t r d −c o n d i t i o n ( n o t ( $BCond0 ) ) = ! ( $ J P r E x p r 0 ) [ t r d −cond −10] $ J P r E x p r 0 : = t r d −c o n d i t i o n ( $BCond0 ) ===> t r d −c o n d i t i o n ( ( $BCond0 ) ) = ( $ J P r E x p r 0 ) [ t r d −cond −5] t r d −c o n d i t i o n ( $BTermSimp0 <= $BTermSimp1 ) = $ J P r E x p r 0 <= $ J P r E x p r 1 Figura 5.20: Regra para a tradução de condições. 106 5.3.7 Instruções Denominam-se de instruções o conjunto de substituições determinísticas da implementação B. Dentre elas, é possível destacar as instruções de definição de variáveis, as condicionais (if e case) e a instrução de repetição while. Salienta-se que as instruções que possuem corpo podem conter outras instruções sob seu escopo. Dessa forma, faz-se necessária a regra de reescrita da figura 5.21, que através da função trd-instructions percorre uma lista de instruções, reescrevendo-as uma a uma. Essa nova reescrita é feita através do casamento com o padrão que define cada instrução em sua regra trd-instructions específica. [ t r d −i n s t r −n ] $JBlockSt1+ := t rd −i n s t r u c t i o n s ( $BInst0 ) , $JBlockSt2+ := t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d −i n s t r u c t i o n s ( $BInst0 ; $ B I n s t s 1 +) = $JBlockSt1+ $JBlockSt2+ Figura 5.21: Equação de reescrita para uma lista de instruções. Destacam-se, inicialmente, as instruções skip, begin-end e becomes equal to (fig. 5.22). A primeira não deve gerar nenhuma construção significativa em Java, dessa forma é traduzida simplesmente para um finalizador de comando (“;”). Por sua vez, instrução begin-end serve apenas ao propósito de demarcar um bloco de instruções, sendo essas traduzidas em uma lista de comandos no código Java. Finalmente, a instrução becomes equal to (:=) é reescrita como uma atribuição em Java. [ t r d −i n s t r − i d e n t i t y ] t r d −i n s t r u c t i o n s ( s k i p ) = ; [ t r d −i n s t r −b e g i n ] $JBlockSt1 + := t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d − i n s t r u c t i o n s ( BEGIN $ B I n s t s 1 + END) = { $JBlockSt1+ } [ t r d −i n s t r −bcmeq −1] $ J P r E x p r 0 : = t r d −t e r m ( $BTerm0 ) ===> t r d − i n s t r u c t i o n s ( $ S e l I d : = $BTerm0 ) = $SelId = $JPrExpr0 ; Figura 5.22: Reescrita das instruções skip, begin-end e becomes equal to. A chamada a uma operação (fig. 5.23) é reduzida a uma sentença semelhante em Java, com o retorno da operação sendo recebido por um identificador do lado esquerdo da atribuição, geralmente o nome de uma variável. Os parâmetros, uma lista de termos separados por vírgula, são traduzidos em expressões através da função auxiliar trd-list-term-comma. A figura 5.23 também apresenta a reescrita de definições de variáveis locais (regra trd-inst-var). Neste caso, a tradução da lista de variáveis é feita em duas etapas no código Java. Em primeiro lugar, os identificadores das variáveis são reescritos em declarações de variáveis locais por meio da 107 função auxiliar trd-listid-to-var-decl (Na equação trd-lid-vardecl-1, a versão para 1 variável). No segundo passo, as instruções mais internas são reescritas. [ t r d −i n s t r −v a r ] $JBlockSt1+ := t r d − l i s t i d −t o −v a r −d e c l ( $ListIdComma1 + ) , $JBlockSt2 + := t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d −i n s t r u c t i o n s ( VAR $ListIdComma1 + IN $ B I n s t s 1 + END) = $JBlockSt1+ $JBlockSt2+ [ t r d −l i d −v a r d e c l −1] t r d − l i s t i d −t o −v a r −d e c l ( $BId1 ) = s h o r t $BId1 ; [ t r d −i n s t r −o p c a l l −1] $JPrExpr1+ := t r d − l i s t −term −comma ( $LTermComma + ) ===> t r d −i n s t r u c t i o n s ( $ S e l I d 0 <−− $ S e l I d 1 ( $LTermComma + ) ) = $SelId0 = $SelId1 ( $JPrExpr1 + ) ; [ t r d −i n s t r −o p c a l l −2] t r d − i n s t r u c t i o n s ( $ S e l I d 0 <−− $ S e l I d 1 ) = $SelId0 = $SelId1 ( ) ; [ t r d −i n s t r −o p c a l l −3] $ J P r E x p r 1 + : = t r d − l i s t −term −comma ( $LTermComma1 + ) ===> t r d − i n s t r u c t i o n s ( $ S e l I d 1 ( $LTermComma1 + ) ) = $SelId1 ( $JPrExpr1 + ) ; Figura 5.23: Reescrita das instruções chamada de operação e definição de variável local. A instrução condicional IF e suas variações são reescritas nas versões equivalentes do condicional if da linguagem Java. Na figura 5.24 apresenta-se a versão do if com o else. A condição de teste é traduzida em uma expressão condicional ou lógica (trd-condition($BCond0)). Em seguida constrói-se o corpo executado caso a condição retorne verdadeiro e, por meio da regra de reescrita trd-else-br, o corpo da parte else. [ t r d −i n s t r −i f −e l s e ] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) , $JBlockSt1 + := t rd −i n s t r u c t i o n s ( $ B I n s t s 1 +) , $ J B l o c k S t 2 ∗ : = t r d −e l s e ( $ E l s e ? ) ===> t r d −i n s t r u c t i o n s ( I F $BCond0 THEN $ B I n s t s 1 + $Else ? END) = i f ( $JPrExpr0 ) { $JBlockSt1+ } else { $JBlockSt2∗ } [ t r d −e l s e −b r ] $JBlockSt1 + := t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d −e l s e ( ELSE $ B I n s t s 1 + ) = $JBlockSt1+ Figura 5.24: Equação de reescrita para condicional (versão com else). As demais variações do condicional if também encontram-se especificadas, uma delas sem a parte else e a outra o if seguido de zero ou mais elsif e, opcionalmente, por um else. Nesse último caso, na equação de reescrita (fig. 5.25), cada parte elsif em B dá origem a um ramo if em Java, sendo esse inserido na parte else do if (ou elsif ) imediatamente anterior. Essa abordagem gera uma instrução condicional encadeada de semântica equivalente ao elsif de B. Ressalta-se que a instrução case (fig. 5.26), açúcar sintático para o condicional if com vários casos aninhados, possui a regra de tradução como o maior número de condições antes 108 [ t r d −i n s t r −i f −e l s i f −e l s e ] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) , $JBlockSt1 + := t rd −i n s t r u c t i o n s ( $ B I n s t s 1 +) , $JBlockSt1 ∗ := trd −e l s i f s ( $ E l s i f 0 ∗) , $ J B l o c k S t 2 ∗ : = t r d −e l s e ( $ E l s e ? ) ===> t r d −i n s t r u c t i o n s ( I F $BCond0 THEN $ B I n s t s 1 + $Elsif0∗ $Else ? END) = i f ( $JPrExpr0 ) { $JBlockSt1+ } else { $JBlockSt1∗ $JBlockSt2∗ } [ t r d −e l s i f −b r ] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) , $JBlockSt1 + := t rd −i n s t r u c t i o n s ( $ B I n s t s 1 +) , $JBlockSt1 ∗ := trd −e l s i f s ( $ E l s i f 0 ∗) ===> trd −e l s i f s ( ELSIF $BCond0 THEN $ B I n s t s 1 + $ E l s i f 0 ∗) = i f ( $JPrExpr0 ) { $JBlockSt1+ } else { $JBlockSt1∗ } Figura 5.25: Equação de reescrita para condicional (versão com elsif). da efetiva aplicação da função trd-instructions que casa com o seu padrão. Caso as condições sejam reduzidas corretamente e o casamento seja estabelecido, a reescrita resulta na instrução switch de Java. [ t r d −i n s t r −c a s e −e l s e ] $JPrExpr0 : = t r d −t e r m ( $BTermSimp0 ) , $JPrExpr1 : = t r d −t e r m ( $BTermSimp1 ) , $JBlockSt1 + := t rd −i n s t r u c t i o n s ( $ B I n s t s 1 +) , $ J S w i t c h B l o c k 0 ∗ : = t r d −o r s ( $Or0 ∗ ) , $ J B l o c k S t 2 + : = t r d −e l s e ( $ E l s e ? ) ===> t r d −i n s t r u c t i o n s ( CASE $BTermSimp0 OF EITHER $BTermSimp1 THEN $ B I n s t s 1 + $Or0 ∗ $Else ? END END) = switch ( $JPrExpr0 ) { case $JPrExpr1 : { $JBlockSt1+ break ; } $JSwitchBlock0∗ default : { $JBlockSt2+ break ; } } [ t r d −c a s e −or−n ] $ J S w i t c h B l o c k 0 ∗ : = t r d −o r ( $Or ) , $ J S w i t c h B l o c k 1 ∗ : = t r d −o r s ( $Or0 ∗ ) ===> t r d −o r s ( $Or $Or0 ∗ ) = $JSwitchBlock0∗ $JSwitchBlock1∗ [ t r d −c a s e −o r ] $JPrExpr0 : = t r d −t e r m ( $BTermSimp0 ) , $JBlockSt1 + := t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d −o r (OR $BTermSimp0 THEN $ B I n s t s 1 + ) = case $JPrExpr0 : { $JBlockSt1+ break ; } Figura 5.26: Reescrita da instrução case. As diversas condições na regra trd-instr-case-else são necessárias para especificar a tradução do case em sua versão completa, composta de uma parte CASE, contendo o termo inicial a ser comparado em cada sub-caso, um EITHER, contendo o termo com o primeiro caso e as instruções a ser executadas se esse termo casar com a expressão inicial, zero ou mais OR’s, com a especificação de outros casos e zero ou um ELSE com o comportamento que deve ser tomado 109 se nenhum dos casos anteriores for satisfeito. Em Java, o termo CASE inicial é reescrito no cabeçalho do switch, e a parte either no primeiro caso do switch. Os demais casos são tratados pelas regras trd-or-n que percorre a lista de casos (parte OR) e trd-case-or, que reescreve efetivamente cada OR em um caso correspondente no switch. A situação else é tratada pela mesma regra de tradução utilizada no IF, sendo as instruções do corpo do ELSE em B inseridas na parte default do switch. Cumpre destacar ainda a tradução da instrução de repetição WHILE (fig. 5.27) no comando while equivalente em Java. Observa-se que os componentes variante e invariante, úteis em B para verificar a correta terminação do laço de repetição, não são traduzidos em Java. [ t r d −i n s t r −w h i l e ] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) , $JBlockSt1 + := t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d −i n s t r u c t i o n s ( WHILE $BCond0 DO $BInsts1+ INVARIANT $BCond1 VARIANT $BTerm0 END) = while ( $JPrExpr0 ) { $JBlockSt1+ } Figura 5.27: Equação para tradução da instrução while. 5.4 Considerações Finais É importante evidenciar que a especificação ASF+SDF tratada neste capítulo corresponde a grande parte das construções da linguagem B traduzidas pela ferramenta de geração de código de suporte ao método BSmart. Ela foi de suma importância para a compreensão do processo de tradução. Além disso, após a sua conclusão, tornou-se uma fonte de documentação a ser disponibilizada para aqueles que se interessarem pela ferramenta, seja como usuário ou desenvolvedor. Apesar de ser possível executar a transformação de um programa em B (termo) para o equivalente em Java Card, essa geração não é totalmente fidedigna devido a limitações da ferramenta Meta-Environment. Por exemplo, não é possível a reescrita em mais de uma classe Java externa. Assim, módulos importados na implementação são reescritos em ASF+SDF como classes internas e sem implementação, apenas para representar que eles irão originar classes. Outro exemplo é a inferência de tipos, bastante simples de se realizar na API BCompiler [Cle12b], mas sem suporte nativo no ambiente ASF+SDF. 110 Durante o processo, a gramática B foi corrigida, com o auxílio da ferramenta MetaEnvironment, para eliminar ambiguidades identificadas em algumas produções e acrescentar construções que estavam faltando, como a instrução while. Além disso, detectou-se uma inconsistência na especificação de termos (expressões) para a linguagem de implementação B0 na documentação oficial da linguagem B fornecida pela Clearsy. Do modo posto, um termo é particionado em diversas subproduções, com o propósito de evitar que termos inadequados sejam inseridos em determinadas expressões. Ao seguir esse modelo de classificação, tornase impraticável traduzir determinadas expressões, como as aritméticas, devido a ambiguidades que surgem no reconhecimento de termos dessas produções. Assim, no SDF, especificou-se a maioria das construções de termos como a produção de mais alto-nível Term, tornando possível a sua tradução. Além dessa questão, apenas pequenas inconsistências foram detectadas, como erros tipográficos na especificação oficial. Apesar do formalismo ASF+SDF contar com uma boa documentação sobre o seu funcionamento e recursos oferecidos, durante o início do processo surgiram dificuldades quanto ao entendimento de como aplicá-lo na especificação da transformação de linguagens. Nesse momento, os exemplos fornecidos pela biblioteca da ferramenta Meta-Environment foram fundamentais, como o que demonstra a tradução de Pico, uma pequena linguagem criada pelos desenvolvedores de ASF+SDF, para a linguagem assembly. Alguns bugs do ambiente MetaEnvironment, que por vezes fazem com que ela tenha que ser reiniciada para que volte a funcionar corretamente, também dificultaram o início da especificação, mas não comprometeram o seu progresso. Observa-se que a atribuição de tipo como um intervalo de inteiros e as estruturas ainda não foram traduzidos. Um intervalo pode ser implementado por uma biblioteca em B que terá uma classe equivalente em Java. Nesse caso teríamos uma máquina de biblioteca (e uma classe) para cada intervalo inteiro permitido. As estruturas, recurso de B implementado pelo Atelier B, equivalem à construção struct na linguagem C, podendo ser traduzidas para uma classe em Java, sendo os elementos internos dessa estrutura atributos da classe. Além da inclusão desses recursos, deve-se realizar, como trabalho futuro, uma revisão completa na gramática B e na tradução tendo em vista verificar se a totalidade da gramática B de implementação que deve ser traduzida está de fato sendo tratada. 111 6 KitSmart: uma biblioteca de auxílio ao desenvolvimento Java Card em B No presente capítulo apresenta-se a biblioteca KitSmart, composta por um conjunto de componentes reutilizáveis para o desenvolvimento de aplicações para smart cards com o método formal B. Destaca-se que os módulos do KitSmart são disponibilizados junto à ferramenta BSmart, mas podem ser reutilizados de forma independente em qualquer outro desenvolvimento em B. Ressalta-se que a biblioteca KitSmart propicia maior agilidade e transparência ao processo de especificação através da inclusão de modelos de tipos de dados primitivos Java/Java Card, de todas as classes e interfaces da API Java Card 2.2.2, e de módulos que tratam de tarefas úteis ao desenvolvimento Java Card, mas que não estão presentes na API padrão. Todas essas especificações B foram verificadas corretas por meio do provador da ferramenta Atelier B. Dessa forma, ao reusá-las em algum projeto, não há a necessidade de se realizar uma nova verificação. Além do suporte ao usuário do método, cumpre frisar que os componentes da biblioteca fornecem importante auxílio ao processo de verificação e de geração de código. No primeiro caso, ao se utilizar os modelos de tipos básicos garante-se que as operações sobre eles estarão restritas aos valores permitidos para o tipo. Por sua vez, a chamada a uma operação de uma classe da API é efetuada verificando-se todas as suas pré-condições, que podem requisitar, por exemplo, a chamada prévia de uma outra operação. Em relação à geração de código, as constantes que representam tipos e as chamadas a um método da API são traduzidas diretamente para os elementos correspondentes na linguagem Java. É importante destacar que este ramo da tese teve início no trabalho de Dutra [Dut06], que desenvolveu os primeiros protótipos dos módulos de tipos primitivos e modelos para lidar com datas, unidades de medida, dentre outros. Posteriormente, durante os primeiros meses desta tese, aprimorou-se as especificações para tipos e desenvolveu-se as primeiras versões de máquinas B que modelam componentes importantes da API Java Card, tais como as classes de exceção e as classes APDU, Util e JCSystem. 112 O trabalho de especificação da API teve continuidade, em parceria com este estudo, na dissertação de Santos [San12]. Nesse trabalho, especificou-se a totalidade das 93 classes/interfaces da API para o Java Card 2.2.2, uma versão recente da API que é amplamente utilizada no mercado. Outra contribuição importante dessa dissertação foi a elaboração de um guia de consulta para usuários e desenvolvedores da API, indicando as melhores práticas, tipos de dados mais adequados e exemplos de uso de diversos componentes. Salienta-se que o estudo de caso do capítulo 7 fez uso de parte da API especificada em [San12], dos modelos para tipos primitivos básicos e de bibliotecas que encapsulam o valor de inteiros e sequências de byte em B. Com relação à API, o estudo do passaporte eletrônico serviu ao seu aprimoramento e validação. Sempre que se verificava necessário em algum componente, modificava-se o estado e/ou a pré-condição das suas operações, visando compatibilizálo à semântica esperada para a classe Java correspondente ou auxiliar a verificação do módulo com as ferramentas de suporte ao método B. O presente capítulo é dividido nas seções descritas a seguir. A seção 6.1 apresenta as máquinas para tipos primitivos e outros componentes de biblioteca utilizados para encapsular o estado da implementação. Em seguida, na seção 6.2, destacam-se os modelos das classes e interfaces da API Java Card. Os módulos de tarefas úteis ao desenvolvimento Java Card são apresentados na seção 6.3. Por fim, as considerações finais e desenvolvimentos futuros são apresentadas na seção 6.4. 6.1 6.1.1 Módulos Básicos Tipos primitivos A biblioteca modela os tipos básicos byte, short e boolean, permitindo a correta atribuição de tipos a variáveis e constantes e contendo um conjunto de operações sobre esses tipos que podem ser verificadas quanto à sua correta utilização. O modelo do tipo int padrão também é fornecido, podendo ser reutilizado pelo desenvolvedor da aplicação host. É interessante observar que os tipos primitivos discutidos nesta seção possuem estrutura similar, constituída por constantes que definem cada tipo e sua faixa de valores, assim como por operações que resultam em valores dentro dessa faixa. Observa-se que tais “operações”, em verdade, foram especificadas na forma de constantes funcionais na cláusula PROPERTIES de cada máquina. Tal abordagem foi adotada em virtude do método B não permitir que, no corpo de uma operação, uma outra operação definida em uma máquina externa seja requisitada 113 mais de uma vez. Portanto, ao utilizar funções ao invés de operações, elimina-se esse problema, permitindo até mesmo o uso de expressões compostas, como, por exemplo, a soma da seguinte forma: sum_byte(sum_byte(value,10),20). Nesse sentido, tem-se como exemplo a figura 6.1, que exibe trechos do componente JShort, modelo para a definição de variáveis short. Observa-se que, no apêndice A, é possível encontrar o código completo da especificação JShort, assim como de parte das máquinas apresentadas neste capítulo. Nessa máquina, a constante JSHORT define o inteiro curto, restrito ao intervalo deste tipo em Java. Em qualquer máquina que inclua JShort, é possível declarar um novo short através dessa constante (p.ex. quant ∈ JSHORT). As funções definidas nesse componente realizam as operações aritméticas básicas, além do módulo, cast e comparações (igual, maior e maior ou igual) entre dois short’s. MACHINE JShort CONCRETE_CONSTANTS MAXSHORT , MINSHORT , JSHORT, sum_short, subt_short, mult_short, div_short, mod_short, cast_short, equal_short, gt_short, gte_short PROPERTIES MAXSHORT ∈ Z ∧ MAXSHORT = 32767 ∧ MINSHORT ∈ Z ∧ MINSHORT = -32768 ∧ JSHORT = MINSHORT .. MAXSHORT ∧ sum_short ∈ JSHORT × JSHORT → JSHORT ∧ sum_short = λ ( n1 , n2 ) . ( n1 ∈ JSHORT ∧ n2 ∈ JSHORT ∧ n1 + n2 ∈ JSHORT | n1 + n2 ) ∧ cast_short ∈ Z → JSHORT ∧ ∀ ( s1 ) . ( s1 ∈ Z =⇒ (s1 = 0 =⇒ cast_short(s1) = 0) ∧ (s1 > 0 =⇒ cast_short(s1) = s1 mod 32767) ∧ (s1 < 0 =⇒ cast_short(s1) = −(−s1 mod 32767)) ) ∧ equal_short ∈ JSHORT × JSHORT → BOOL ∧ ∀ ( s1 , s2 ) . ( s1 ∈ JSHORT ∧ s2 ∈ JSHORT =⇒ ( ( s1 = s2 ) =⇒ equal_short ( s1 7→ s2 ) = TRUE ) ∧ ( ( s1 6= s2 ) =⇒ equal_short ( s1 7→ s2 ) = FALSE ) ) ∧ (...) END Figura 6.1: Modelo em B para o tipo short. As máquinas para int (JInt) e byte (JByte) são similares a JShort, alterando-se apenas o nome da constante que identifica o tipo, sua faixa de valores e o nome das funções, que recebem o sufixo _jint ou _jbyte. No caso do componente que representa o tipo inteiro para Java Card (JCInt), desenvolveu-se uma especificação que o implementa através de dois valores do tipo short. A especificação para o tipo primitivo boolean (fig. 6.2) define o conjunto JBOOLEAN em 114 função do tipo BOOL em B e as operações lógicas básicas sobre operandos booleanos, tais como, conjunção, disjunção e negação. MACHINE JBoolean CONCRETE_CONSTANTS JBOOLEAN, or_boolean, lor_boolean, and_boolean, land_boolean, lnot_boolean, lxor_boolean PROPERTIES JBOOLEAN = BOOL ∧ and_boolean ∈ JBOOLEAN × JBOOLEAN → JBOOLEAN ∧ ∀ ( b1 , b2 ) . ( b1 ∈ JBOOLEAN ∧ b2 ∈ JBOOLEAN ∧ b1 = FALSE =⇒ and_boolean ( b1 7→ b2 ) = FALSE ) ∧ ∀ ( b1 , b2 ) . ( b1 ∈ JBOOLEAN ∧ b2 ∈ JBOOLEAN ∧ b1 = TRUE ∧ b2 = FALSE =⇒ and_boolean ( b1 7→ b2 ) = FALSE ) ∧ ∀ ( b1 , b2 ) . ( b1 ∈ JBOOLEAN ∧ b2 ∈ JBOOLEAN ∧ b1 = TRUE ∧ b2 = TRUE =⇒ and_boolean ( b1 7→ b2 ) = TRUE ) ∧ (...) lnot_boolean ∈ JBOOLEAN → JBOOLEAN ∧ lnot_boolean = { TRUE 7→ FALSE , FALSE 7→ TRUE } ∧ (...) END Figura 6.2: Parte da máquina JBoolean. 6.1.2 Outros Módulos de Biblioteca Cumpre ressaltar que a especificação KitSmart para modelos básicos ainda inclui máquinas de biblioteca para encapsular o estado da implementação. Foram definidos componentes para os tipos básicos inteiros e sequências de bytes. No mínimo, cada um desses módulos deve possuir operações para atribuir valores ao estado e para consultá-lo. É possível que os módulos oferecidos sejam alterados para algum propósito particular. Por exemplo, a figura 6.3 exibe a biblioteca para uma sequência de bytes, utilizada no estudo de caso do passaporte eletrônico (capítulo 7). Nesse caso, a inicialização da máquina foi modificada para inicializar uma sequência de 8 bytes com o valor zero. Observa-se que além das operações para inserir e obter uma sequência armazenada, outras operações úteis são fornecidas, permitindo, por exemplo, a inserção de valores em um posição da sequência (addFirst, set (ii, vv)), obter o seu tamanho (numberOfElements), e verificar se ela está cheia (isFull). 6.2 Especificação das classes da API Java Card A versão atual desta parte do KitSmart foi desenvolvida tendo como guia principal a documentação da API Java Card 2.2.2 [SM10]. Outras referências importantes, descritas em maiores 115 MACHINE LM_SEQ_1 SEES JByte, JShort CONSTANTS BT_maxsize PROPERTIES BT_maxsize ∈ JSHORT ∧ BT_maxsize = MAXSHORT- 1 VARIABLES bt_seq INVARIANT bt_seq ∈ seq(JBYTE) ∧ size(bt_seq) ≤ BT_maxsize INITIALISATION bt_seq := [0, 0, 0, 0, 0, 0, 0, 0] OPERATIONS nn ← numberOfElements = BEGIN nn := size(bt_seq) END ; bb ← isFull = BEGIN bb := bool(size(bt_seq) = BT_maxsize) END ; vv ← get (ii) = PRE ii ∈ JSHORT ∧ ii ∈ 1 .. size(bt_seq) THEN vv := bt_seq(ii) END ; vv ← getSeq = BEGIN vv := bt_seq END; addFirst (vv) = PRE vv ∈ JBYTE ∧ size(bt_seq) < BT_maxsize THEN bt_seq:= vv → bt_seq END ; set (ii, vv) = PRE ii ∈ JSHORT ∧ ii ∈ 1 .. size(bt_seq) ∧ vv ∈ JBYTE THEN bt_seq(ii) := vv END ; (...) END Figura 6.3: Definição do estado e algumas operações da biblioteca para sequência de byte. detalhes no capítulo 8, foram a especificação da classe APDU em JML [LC06], descrita no trabalho de Meijer e Poll [MP01] e o trabalho de mestrado de Larson [Lar03], que desenvolveu a especificação de parte das classes da API 2.2.1 na linguagem Object Constraint Language (OCL) [OMG10]. Na abordagem proposta nesta tese, os modelos da API Java Card aplicam-se a três propósitos principais, a saber: (i) prover módulos B verificados de todas as classes e interfaces da versão Java Card 2.2.2, (ii) permitir a verificação do uso adequado desses módulos em relação aos dados recebidos e a dependências na chamada de uma operação e (iii) facilitar o trabalho da ferramenta geradora de código de B para Java, que irá traduzir uma chamada de operação ou o acesso a uma constante na construção equivalente em Java. Inicialmente, durante o início da tese, foram criadas as primeiras versões dos modelos de classes importantes da API, na versão 2.2.1, tais como APDU, JCSystem, Util e ISO7816, classes de exceção, dentre outras. A primeira é uma das classes mais importantes da API. Através dela é possível acessar o buffer APDU para envio e recepção de informações na classe applet. Já a classe JCSytem é responsável, dentre outras funções, por delimitar um trecho de código sob controle de transação. A classe Util, por sua vez, contém operações diversas, tais como obter um short a partir de um array de bytes e realizar a cópia de um array em outro. Com relação às classes de exceção, havia sido especificado apenas o componente Exception, contendo um 116 método throwIt representando o lançamento de uma exceção. Salienta-se que o trabalho de Santos [San12] contribuiu com a presente tese ao aprimorar as máquinas desenvolvidas anteriormente, compatibilizando-as com a versão 2.2.2, revisando e alterando (quando necessário) as especificações, em particular as pré-condições das operações. As alterações eram motivadas pela experiência prática do uso e teste dessas especificações no estudo de caso do capítulo 7. É importante destacar que em [San12] foram especificadas todas as 93 classes/interfaces da API, divididas entre os pacotes da figura 6.4. Figura 6.4: Quantidade de classes por pacote na API 2.2.2. Fonte [San12]. Nas seções seguintes são apresentadas as contribuições recentes à API. O modelo da classe APDU (seção 6.2.2) será utilizado para ilustrar as abordagens da API para tratar diversas situações. Dentre elas, é possível destacar as exceções (seção 6.2.3), os tipos abstratos de dados (seção 6.2.4) e os métodos sobrecarregados e sobrescritos (seção 6.2.5). 6.2.1 Características gerais dos modelos Nesta seção enfatizam-se algumas características comuns aos diversos módulos e diretrizes gerais utilizadas na modelagem da API. Em primeiro lugar, nas operações de cada módulo, decidiu-se por fornecer a especificação da interface, das pré-condições e uma implementação mínima em seu corpo. Nesse último caso, são especificados o tipo esperado de retorno (quando houver) e, em algumas máquinas (por exemplo, Apdu e JCSystem), a atualização no estado da especificação. Essa especificação mínima em cada operação se deve ao fato da API já se encontrar desenvolvida em Java, e da especificação das restrições e possíveis situações de erro em sua pré-condição ser suficiente, na maioria dos modelos (exceto os que possuem estado interno), para as verificações durante o uso de uma operação. Por esses mesmos motivos, as máquinas que modelam a API não foram refinadas. Visando documentar as operações, incluem-se em cada uma delas os comentários fornecidos no Javadoc da API para o seu método correspondente. Essa abordagem também permite 117 associar uma pré-condição a uma restrição observada no método, sendo as restrições, no código Java, normalmente relacionadas a um lançamento de exceção caso não sejam obedecidas. Para aqueles comportamentos excepcionais que não podem ser antecipados, tais como exceções associadas a erros durante a execução, por exemplo, durante uma interação com o JCRE, os comentários inclusos servem para alertar ao usuário que elas podem ocorrer em determinadas situações. 6.2.2 Exemplo: modelo da classe APDU Como exemplo de especificação de uma classe da API, apresenta-se nesta seção a máquina que modela a classe APDU. Consoante apresentado no capítulo 2 (seção 2.1), essa classe é um dos principais componentes da API Java Card, sendo responsável por encapsular toda a comunicação com a aplicação host. O seu modelo B é dividido entre as máquinas Apdu e Apdu_Properties. Essa última, exibida na figura 6.5, contém as definições estáticas do módulo Apdu, tais como as constantes que representam o estado em que a comunicação se encontra (constantes que iniciam com STATE_). MACHINE Apdu_Properties SEES JByte, JShort SETS /* Conjunto que representa o tipo APDU */ APDU CONCRETE_CONSTANTS STATE_INITIAL, STATE_PARTIAL_INCOMING, STATE_FULL_INCOMING, STATE_OUTGOING, STATE_OUTGOING_LENGTH_KNOWN, STATE_PARTIAL_OUTGOING, STATE_FULL_OUTGOING, (...) BUFFER_LENGTH PROPERTIES STATE_INITIAL ∈ JBYTE ∧ STATE_INITIAL = 0 ∧ STATE_PARTIAL_INCOMING ∈ JBYTE ∧ STATE_PARTIAL_INCOMING = 1 ∧ STATE_FULL_INCOMING ∈ JBYTE ∧ STATE_FULL_INCOMING = 2 ∧ STATE_OUTGOING ∈ JBYTE ∧ STATE_OUTGOING = 3 ∧ STATE_OUTGOING_LENGTH_KNOWN ∈ JBYTE ∧ STATE_OUTGOING_LENGTH_KNOWN = 4 ∧ STATE_PARTIAL_OUTGOING ∈ JBYTE ∧ STATE_PARTIAL_OUTGOING = 5 ∧ STATE_FULL_OUTGOING ∈ JBYTE ∧ STATE_FULL_OUTGOING = 6 ∧ (...) BUFFER_LENGTH ∈ JSHORT ∧ BUFFER_LENGTH ≥ 0 ∧ BUFFER_LENGTH ≤ 133 END Figura 6.5: Máquina Apdu_Properties. Cumpre observar que a divisão do componente APDU em duas especificações foi necessária 118 para evitar o conflito de nomes dos conjuntos e constantes declarados em Apdu_Properties, no momento em que um módulo que inclui Apdu, incluísse outro módulo que também referencia Apdu. Isso é possível de acontecer mesmo que o mecanismo de renomear cada instância seja utilizado, pois a renomeação não se aplica a esses componentes estáticos. Dessa forma, uma máquina que necessite usar o módulo APDU, deve acrescentar em sua cláusula INCLUDES a referência à máquina Apdu (com a definição de variáveis e operações) e, na cláusula SEES, a especificação Apdu_Properties. A figura 6.6 exibe a parte da máquina Apdu relacionada à definição das suas variáveis de estado. A cláusula EXTENDS nesse módulo representa a hierarquia de herança para o componente APDU, que herda diretamente a classe Object. Vale relembrar que ao referenciar uma máquina em EXTENDS, todas as suas operações tornam-se operações do módulo que a estende. Em todos os outros módulos o mecanismo de extensão é utilizado, fazendo com que o modelo se aproxime ainda mais da semântica que é encontrada na API Java Card. Observa-se que a variável state armazena o estado corrente da especificação, inicializada com o valor STATE_INITIAL e alterado com a aplicação de operações da máquina. A definição do estado é um requisito observado pela documentação da classe, tendo em vista assegurar a ordem correta na chamada a operações quando o applet precisa receber ou enviar informações. Por sua vez, o buffer APDU é representado como uma sequência de bytes pela variável buffer. As variáveis lc e le e lr relacionam-se ao número de bytes enviados pelo cliente (lc) e pela aplicação cartão (le e lr). MACHINE Apdu EXTENDS Object SEES JShort, JByte, APDU_Properties, ISO7816 CONCRETE_VARIABLES state, buffer, lc, le, lr INVARIANT state ∈ STATE_INITIAL .. STATE_FULL_OUTGOING ∧ buffer ∈ seq(JBYTE) ∧ size(buffer) ≥ 0 ∧ size(buffer) ≤ 133 ∧ lc ∈ JSHORT ∧ lr ∈ JSHORT ∧ le ∈ JSHORT ∧ le ≥ 0 ∧ le ≤ 256 INITIALISATION buffer := [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] || state := STATE_INITIAL || lc, le, lr := 0, 0, 0 Figura 6.6: Definição do estado da máquina Apdu. 119 Na figura 6.7 apresentam-se as operações setOutgoing, setOutgoingLength e sendBytes, relacionadas ao envio de informações para o cartão. Nas pré-condições são inseridas as restrições aos valores recebidos nos parâmetros. Normalmente, esses valores se relacionam a situações que podem gerar exceções, conforme detalhado na seção 6.2.3, bem como ao estado que deve ser observado antes da aplicação da operação. Observa-se que a documentação associada a cada operação foi omitida, mas pode ser consultada no código completo da máquina Apdu incluso no apêndice A.3. A implementação no corpo das operações da API está relacionada à pós-condição que deve ser observada após a sua aplicação. No caso do exemplo, modifica-se o estado da comunicação entre as aplicações host e cartão através da variável state para o estado posterior à aplicação da operação. Os valores esperados de retorno também são especificados, como no caso de setOutgoing, que retorna o valor do tamanho esperado da resposta (le). result ← setOutgoing = PRE (state = STATE_INITIAL ∨ state = STATE_PARTIAL_INCOMING ∨ state = STATE_FULL_INCOMING) THEN state := STATE_OUTGOING || result := le END; setOutgoingLength(len) = PRE len ∈ JSHORT ∧ len ≥ 0 ∧ len ≤ 256 ∧ state = STATE_OUTGOING THEN state := STATE_OUTGOING_LENGTH_KNOWN || lr := len END; sendBytes(bOff, len) = PRE bOff ∈ JSHORT ∧ bOff ≥ 0 ∧ len ∈ JSHORT ∧ len ≥ 0 ∧ len ≤ lr ∧ sum_short(bOff,len) ≤ BUFFER_LENGTH ∧ state = STATE_OUTGOING_LENGTH_KNOWN THEN CHOICE state := STATE_PARTIAL_OUTGOING OR state := STATE_FULL_OUTGOING END || lr := subt_short(lr, len) END; Figura 6.7: Operações da máquina Apdu relacionadas ao envio de informações ao cliente. 6.2.3 Abordagem para exceções Observa-se que o lançamento de exceções é um importante tópico relacionado ao desenvolvimento Java Card, sendo o meio principal para comunicar à aplicação cliente que um erro ocor- 120 reu devido a uma entrada de dados inadequada ou a um comportamento interno não-previsto. O cliente é informado sobre o processamento errôneo de um comando através do código de status (um valor short) retornado, inserido na reposta por uma das classes de exceção ou pelo próprio ambiente de execução (JCRE). Um status diferente de 0x9000 indica a ocorrência de um erro durante o processamento de um serviço. Neste trabalho, utiliza-se o modelo robusto de antecipar as exceções que podem ser previstas, conforme indicado na documentação oficial da API, incluindo-se na pré-condição um predicado relacionado à exceção e o comentário associado que é encontrado na documentação. Assim, a restrição adicionada irá gerar uma ou mais obrigações de prova quando a operação for utilizada em algum módulo B, que deve então descartá-la de algum modo. Como exemplo, a figura 6.8 exibe a versão completa da operação setOutgoingLength, com os predicados de exceção e seus comentários associados adicionados na pré-condição. Geralmente, em um refinamento, um condicional é utilizado, contendo na condição de teste a(s) restrição(ões), fazendo com que a operação seja aplicada apenas se elas forem atendidas. Em caso contrário, normalmente reporta-se a exceção ao cliente. setOutgoingLength(len) = PRE len ∈ JSHORT ∧ /*APDUException if ’len’ is negative*/ len ≥ 0 ∧ /* APDUException if ’len’ is greater than 256 and the currently selected applet does not implement the javacardx.apdu.ExtendedLength interface */ len ≤ 256 ∧ state = STATE_OUTGOING /* APDUException if setOutgoing() or setOutgoingNoChaining() not called or if setOutgoingAndSend() already invoked, or this method already invoked */ /* APDUException I/O error */ THEN state := STATE_OUTGOING_LENGTH_KNOWN || lr := len END; Figura 6.8: Condições de exceção e seus comentários associados na pré-condição da operação setOutgoingLength. 6.2.4 Atribuição de tipos a objetos É sabido que B não fornece suporte à declaração de tipos abstratos de dados que associem um nome a uma estrutura contendo dados e operações relacionados, tal como uma classe em Java. O mais próximo que se pode chegar em B é declarar uma variável como uma estrutura (struct), recurso presente na linguagem B fornecida pelo Atelier B. Nesse caso, apenas o estado do tipo é armazenado nos campos da estrutura, semelhante à struct de C. 121 Neste trabalho, tentou-se inicialmente representar o estado de um componente da API como uma constante do tipo estrutura (struct), permitindo o seu uso para representar o tipo abstrato na declaração de uma variável ou parâmetro. Esse procedimento foi utilizado no módulo Apdu (figura 6.9), no qual todas as variáveis de estado foram incorporadas à estrutura APDU, utilizada para tipar a variável this que encapsula o estado da máquina. O último campo é utilizado para declarar se uma instância desse tipo é ou não nula (null). Observa-se que uma instância é criada com a instrução rec, que atribui valores para cada campo da estrutura. No exemplo, cria-se uma instância de APDU na inicialização da máquina, salvando-a na variável this. MACHINE Apdu_v2 EXTENDS Object SEES JShort, JByte, APDU_Properties, ISO7816 CONSTANTS APDU PROPERTIES APDU = struct (buffer ∈ seq(JBYTE), state ∈ JBYTE, lc ∈ JSHORT, le ∈ JSHORT, lr ∈ JSHORT, null ∈ BOOL) CONCRETE_VARIABLES this INVARIANT this ∈ APDU ∧ this’le ≥ 0 ∧ this’le ≤ 256 ∧ this’state ∈ APDU_STATE ∧ size(this’buffer) ≥ 0 ∧ size(this’buffer) ≤ BUFFER_LENGTH INITIALISATION this := rec (buffer : [], state : STATE_INITIAL, lc : 0, le : 0, lr : 0, null : FALSE) (...) END Figura 6.9: Definição do estado da máquina APDU com uma estrutura representando o tipo APDU. Ocorre que o provador da ferramenta Atelier B, talvez por algum bug na versão 4.01 utilizada no desenvolvimento do trabalho, não consegue provar, na implementação B, os lemmas para estruturas. Dessa forma, preteriu-se essa solução pela representação do tipo abstrato como um conjunto, conforme pode-se vislumbrar na figura 6.5, que contém a constante APDU para representar o tipo APDU. Ressalta-se que essa abordagem ainda deve ser alvo de um estudo mais aprofundado como trabalho futuro, incluindo-se a investigação da falha apresentada pela ferramenta de verificação ao se trabalhar com estruturas. 6.2.5 Sobrecarga e sobrescrita de métodos Cumpre destacar que a sobrecarga de métodos não é permitida no formalismo B, assim, o nome de uma operação só pode aparecer uma única vez em um módulo. Diante dessa restrição, métodos sobrecarregados em Java Card, tais como construtores, foram sufixados no seu modelo em B com um sublinhado seguido de um número para diferenciá-los, conforme exemplificado pelos métodos register do modelo Applet (fig. 6.10). Durante o desenvolvimento do estudo de caso sentiu-se a necessidade de fornecer a im- 122 MACHINE Applet (...) OPERATIONS register_1 = BEGIN skip END; register_2 (bArray, bOffset, bLength) = PRE bArray ∈ seq(JBYTE) ∧ bOffset ∈ JSHORT ∧ bOffset ≥ 0 ∧ bLength ∈ JBYTE ∧ bLength ≥ 5 ∧ bLength ≤ 16 THEN skip END; (...) END Figura 6.10: Duas versões do método register da máquina Applet. plementação de uma operação correspondente a um método abstrato em Java. Trata-se do método process da classe Applet. Nesse caso, a sobrescrita foi realizada como um refinamento, incluindo-se a nova implementação no método refinado (fig. 6.11). Com esta abordagem é possível alinhar a especificação à semântica encontrada em Java Card, em que o applet herda de javax.framework.Applet. REFINEMENT JCApplet_ref REFINES Applet (...) OPERATIONS (...) process (apdu)= BEGIN skip END; IMPLEMENTATION JCApplet_imp REFINES JCApplet_ref (...) OPERATIONS (...) process (apdu) = VAR bf, cla, ins, p1, p2, lc, data_in, lr, data_out, apdu_state, res, res2 IN bf ← apdu.getBuffer; cla := cast_byte (0); ins := cast_byte (0); p1 := cast_byte (0); p2 := cast_byte (0); lc := cast_byte (0); (...) END Figura 6.11: Sobrescrita do método process como um refinamento de Applet. 6.3 Módulos para tarefas úteis ao desenvolvedor Os módulos destacados nesta seção visam fornecer ao KitSmart modelos que podem ser úteis ao desenvolvimento smart card, que muitas vezes são parte de APIs para a linguagem Java, mas que não encontram-se implementados na API Java Card padrão. Nessa categoria de biblioteca é possível incluir componentes que lidam com tempo (data, hota, etc.), internacionalização, unidades de medida, dentre outros. O trabalho de Dutra desenvolveu versões iniciais dos módulos Date (fig. 6.12), contendo operações com datas, Time, semelhante à anterior, mas aplicada a tempo (horas, minutos e se- 123 gundos), Measure, voltada à manipulação de medidas com números reais e Person, que trata do armazenamento de informações pessoais (nome, cpf, endereço, etc.). Na abordagem utilizada, cada modelo em B é acompanhado da sua classe correspondente em Java Card, geradas a partir da sua implementação B com a ferramenta BSmart. Assim, asseguram-se as verificações com o uso do módulo em B e o reuso da classe para aquele módulo no código gerado para Java, sem a necessidade de sintetizá-lo novamente para cada nova aplicação. MACHINE Date SEES JShort, JBoolean CONCRETE_VARIABLES day, month, year INVARIANT day ∈ JSHORT ∧ day ∈ 1 .. 31 ∧ month ∈ JSHORT ∧ month ∈ 1 .. 12 ∧ year ∈ JSHORT ∧ year ∈ 1 .. 30000 INITIALISATION day := 1 || month := 1 || year := 1 OPERATIONS setDate(dd,mm,yy) = PRE dd ∈ JSHORT ∧ dd ∈ 1 .. 31 ∧ mm ∈ JSHORT ∧ mm ∈ 1 .. 12 ∧ yy ∈ JSHORT ∧ yy ∈ 1 .. 30000 ∧ (dd = 31 =⇒ mm ∈ {1,3,5,7,8,10,12}) ∧ (mm = 2 =⇒ (dd ≤ 28 ∨ (dd = 29 ∧ (mod_short(yy,400) = 0 ∨ (mod_short(yy,4) = 0 ∧ mod_short(yy,100) 6= 0))))) THEN day := dd || month := mm || year := yy END; res ← getDay = BEGIN res := day END; res ← getMonth = BEGIN res := month END; res ← getYear = BEGIN res := year END; (...) END Figura 6.12: Parte da máquina Date. Devido ao tempo necessário requerido pela especificação da API Java Card, na dissertação de Santos, os componentes de biblioteca foram apenas atualizados, o que compreendeu compatibilizar a sua sintaxe com o Atelier B e incluir as versões mais recentes das máquinas de tipos. 6.4 Considerações Finais e Desenvolvimentos Futuros Neste capítulo apresentou-se a biblioteca KitSmart, que disponibiliza modelos para tipos de dados, componentes reutilizáveis com tarefas úteis ao desenvolvedor e máquinas correspondentes a todas as classes da API Java Card na versão 2.2.2. 124 Sobre tópicos que devem ser melhor pesquisados, ressalta-se a representação de tipos de dados abstratos (objetos), o que atualmente é feito através de conjuntos com o nome do tipo. A abordagem de utilizar estruturas em B pode ser uma solução, apesar de ter apresentado falhas no momento da verificação com a versão 4.01 da ferramenta. A investigação das causas dessa falha também faz parte do trabalho, e poderá indicar a continuidade ou não da solução de utilizar estruturas. Enfatiza-se que, recentemente, foi lançada a especificação Java Card 3.0 [Ora12]. Nessa nova versão, o desenvolvimento de que trata o presente trabalho é denominado de classic, sendo apenas uma atualização da especificação 2.2.2. Como novidade, tem-se um modelo de desenvolvimento denominado de connected, no qual a aplicação cartão pode ser implementada como um servlet, recebendo e enviando informações via protocolo http. O Java Card connected aproveita a capacidade de cartões recentes, com processadores de 32 bits (8 bits na 2.2.2) e maior capacidade de memória volátil e não-volátil, o que permite o suporte a recursos recentes da linguagem Java. Atualmente, é possível compilar para o Java 6, com suporte a execução concorrente (threads), Strings, o tipo int, enum, generics, coleta de lixo, dentre outras. Como trabalhos futuros deve-se fornecer a especificação para a API classic e investigar que contribuições podem ser oferecidas ao Java Card 3.0 connected. Com respeito ao suporte de bibliotecas para tarefas úteis ao desenvolvedor Java Card, é importante enfatizar que, até o presente momento, eles ainda encontram-se tal qual implementados em [Dut06]. Em [San12], foram feitas apenas atualizações sintáticas para a notação do Atelier B, bem como atualizou-se a referência às bibliotecas básicas de tipos. Na continuação do desenvolvimento desse ramo do KitSmart, pretende-se investigar outros módulos que devem ser inseridos, como por exemplo, um modelo para internacionalização (códigos de países, moedas, etc.), possivelmente derivado de classes da API Java padrão. As máquinas, refinamentos, e classes já implementadas devem passar por uma revisão e ser objeto de testes. Outro ponto a ser observado diz respeito à interação do ambiente de execução Java Card com alguns componentes, tais quais as classes APDU e JCSystem. Esse procedimento pode ocorrer, por exemplo, quando a classe APDU recebe um comando a ser processado. É importante deixar claro que, na proposta deste trabalho, esses detalhes da interação das classes com o ambiente de execução não foram modelados. Entretanto, deve-se pesquisar se essas interações devem ser especificadas (e a melhor forma de fazê-lo), tendo em vista que parte dos erros (exceções) gerados durante a execução tem origem durante a comunicação do JCRE com as aplicações. 125 7 Estudo de Caso: Passaporte Eletrônico O primeiro passo na direção de automatizar a leitura e identificação de um documento de viagem data de 1968. Nesse ano, a ICAO (International Civil Aviation Organization) [ICA11] propôs que a leitura ótica dos dados contidos no documento poderia ser utilizada para agilizar a identificação de passageiros nos controles de imigração dos aeroportos [ICA06]. No entanto, a operacionalização do sistema foi concluída apenas em meados dos anos 80, tornando-se amplamente utilizado até os dias atuais. Ressalte-se que, ao longos dos anos, diversos mecanismos físicos de segurança vêm sendo incorporados aos passaportes, como marcas d’água, tintas especiais (fluorescentes, sensíveis a infravermelho, etc.), adesivos holográficos, dentre outros. Essas tecnologias têm por objetivo impedir (ou, ao menos, dificultar) falsificações, tais como a cópia não autorizada de partes do documento, a simulação das técnicas de manufatura do passaporte visando reproduzí-lo e a adulteração de um documento original válido, substituindo-se os dados pessoais do portador ou a sua fotografia. Com o objetivo de aprimorar ainda mais a segurança dos documentos de viagem, provendo mecanismos que permitam maiores garantias de autenticidade e confidencialidade, a partir de 1998, a ICAO estabeleceu um grupo de trabalho que tem por missão o estudo e especificação de novos sistemas de identificação biométrica e os meios de armazenamento seguro dessas informações nos passaportes. O grupo de trabalho, denominado de New Technologies Working Group (NTWG), ficou responsável por definir os mecanismos de identificação biométrica que seriam incorporados ao documento, qual meio físico de armazenamento seria utilizado e, ainda, a estrutura de dados que seria utilizada para a padronização global do armazenamento e recuperação de arquivos no passaporte. Impulsionado pelos trágicos acontecimentos do dia 11 de setembro de 2001, que expôs a fragilidade dos sistemas de segurança dos aeroportos, o trabalho do NTWG foi finalizado em 2003, e pode ser consultado no volume II do documento Machine Readable Travel Documents: 126 Part I - Machine Readable Passports [ICA06], que contém as especificações oficiais do passaporte eletrônico. De acordo como a especificação oficial, o ePassport é um documento de viagem que está em conformidade com a primeira parte da especificação (caracterísiticas físicas, mecanismos físicos de segurança, etc.) e, além disso, possui um cartão inteligente (smart card) sem contato (comunicação por rádio-frequência - RFID) embutido, dentro do qual são armazenadas as informações presentes na página de dados do passaporte e alguma informação para identificação biométrica do portador, tal como a sua fotografia (obrigatória) e, opcionalmente, imagens de impressões digitais e íris. Essas informações devem ser obtidas e armazenadas de acordo com as normas da ICAO e protegidas pelos diversos protocolos de segurança que podem estar presentes no sistema do passaporte (seção 7.2.4). É importante observar que apesar de não ser obrigatório que os passaportes emitidos sejam eletrônicos, a adoção do sistema de dados biométricos armazenados de forma segura em um cartão inteligente tem sido gradativamente implementada pelas nações ao redor do mundo para aumentar a segurança na identificação, minimizar o risco de fraudes e agilizar os procedimentos de controle migratório e expedição de outros documentos de viagem, como vistos. Atualmente, diversos países já utilizam-se do passaporte eletrônico, como os países membros da União Européia, Estados Unidos e, a partir de fevereiro de 2011, o Brasil, com a implementação de um modelo de passaporte eletrônico semelhante ao europeu. Neste capítulo é apresentado um modelo formal do passaporte eletrônico fundamentado na especificação oficial do passaporte (Volumes I e II), e no trabalho de Erick Poll et al. [MP10], que implementou, na linguagem Java Card, uma versão de código aberto do passaporte eletrônico, denominada de JMRTD (Java Card Machine Readable Travel Document). O desenvolvimento formal do passaporte eletrônico vem sendo utilizado na validação do processo de desenvolvimento com o método BSmart, bem como das ferramentas que fornecem suporte a ele. A especificação também foi importante no aprimoramento da biblioteca KitSmart, uma vez que os tipos básicos de dados e as diversas máquinas que modelam a API JavaCard foram utilizadas na especificação, conforme demonstrado na seção 7.7. Este capítulo é dividido conforme descrito a seguir. A seção 7.1 apresenta os principais trabalhos e a documentação utilizada como referência para o estudo de caso. Na seção 7.2 detalha-se a aplicação de passaporte eletrônico, sua estrutura de armazenamento de dados e seus protocolos de segurança. Na seção 7.3 é descrita a especificação textual dos requisitos funcionais dos serviços do passaporte que foram implementados. O desenvolvimento formal do passaporte tem início na seção 7.4, que descreve a máquina abstrata Passport. Por sua vez, 127 o componente Passaport_JC, cujo desenvolvimento resultará na aplicação cartão, é apresentado na seção 7.5 e o seu refinamento full function na seção 7.6. O refinamento concreto do desenvolvimento é relatado na seção 7.7. Na sequência, a seção 7.8 trata da verificação da conformidade entre o modelo abstrato e a especificação Java Card. Por fim, na seção 7.9, são tecidas as considerações finais e desenvolvimentos futuros. 7.1 Principais referências utilizadas A especificação principal do passaporte eletrônico é fornecida pela ICAO no documento Machine Readable Passports, volumes I e II [ICA06]. O primeiro volume especifica a composição do documento, os elementos de segurança inseridos nas folhas do passaporte (tintas especiais, hologramas, etc.), as informações que devem estar presentes na folha de dados do portador, bem como a disposição delas na zona de leitura ótica (Machine Readable Zone - MRZ), contida nessa mesma página. O segundo volume, mais interessante para o propósito deste trabalho, especifica os elementos relacionados ao passaporte eletrônico em si, o que compreende a definição de um smart card como meio de armazenamento e recuperação das informações pessoais e biométricas, a estrutura de dados utilizada para a codificação dessas informações, e os protocolos e mecanismos de segurança que em conjunto asseguram a sua proteção, integridade e autenticidade. Outra referência importante é o trabalho de Erik Poll e do seu grupo de pesquisa da Universidade de Radboud [OOS11]. Eles desenvolveram uma implementação open source do passaporte eletrônico utilizando a linguagem Java Card com base na documentação da ICAO. Essa implementação, denominada de Java Machine Readable Travel Document, implementa os protocolos presentes no modelo de passaporte europeu e permite a consulta aos dados armazenados através de uma aplicação terminal desenvolvida em Java. É importante ressaltar que a documentação oficial do passaporte é extensa e, em alguns momentos, de difícil leitura para aqueles que estão tentando utilizá-la como base para uma implementação. Por exemplo, a interação entre os diversos protocolos não é detalhada na documentação, deixando o desenvolvedor com diversas possibilidades de combinação. Em uma tentativa de melhor organizar as ideias contidas nessas especificações oficiais, Mostowsky e Poll, também autores da implementação open source do passaporte descrita anteriormente, publicaram o relatório técnico [MP10] que descreve, utilizando diagramas de estado, cada protocolo e as mensagens envolvidas na comunicação entre a aplicação terminal e a aplicação cartão (applet). Alguns pontos que não estão claros na documentação original também são discuti- 128 dos, como o que fazer em caso de falha na execução de um comando ou algumas das possíveis combinações de protocolos. 7.2 Passaporte Eletrônico O passaporte eletrônico é um documento de viagem que obedece à especificação oficial da ICAO, volume I, e além disso, possui um circuito integrado smart card embutido. O cartão contém as informações da zona de leitura ótica (MRZ) da página de dados do portador do passaporte, assim como dados biométricos, que podem ser lidos através de terminais que se comunicam com o smart card por rádio frequência, ou seja, sem contato físico com o cartão. Protocolos especificados pela ICAO garantem a segurança na comunicação entre o terminal o cartão, assim como a integridade e a autenticidade das informações armazenadas através de mecanismos como hashing e criptografia de chave pública. Em relação aos dados biométricos, a recomendação é de que estejam presentes no smart card: • Uma imagem digital do portador em alta-resolução (obrigatório); • Impressão digital (opcional); • imagens da íris (opcional). O reconhecimento facial foi definido como o meio biométrico padrão para permitir a interoperabilidade entre os sistemas de diversos países, tendo em vista que esse é um meio de identificação amplamente utilizado e socialmente aceito. Os outros dois métodos podem ser utilizados como elementos adicionais, dessa forma aumentando a segurança e a precisão na identificação. No caso do passaporte brasileiro, o meio adicional utilizado é o armazenamento da impressão digital de dois dedos do portador do documento. As seções a seguir detalham cada aspecto relevante das características do passaporte. Os componentes da página de dados são descritos na seção 7.2.1. Por sua vez, a seção 7.2.2 apresenta a estrutura de dados utilizada para o armazenamento das informações no cartão. É importante salientar que cada informação ou elemento biométrico armazenado possui o acesso restrito por um determinado protocolo ou combinação de protocolos. A seção 7.2.3 introduz os mecanismos de autenticação, integridade e segurança presentes no passaporte eletrônico, sendo o conjunto protocolos que se utiliza desses mecanismos descrito em detalhes na seção 7.2.4. 129 7.2.1 Página de dados do passaporte A página de dados do passaporte (figura 7.1) contém informações que identificam o seu portador, como o seu nome, número do passaporte, nacionalidade, data de expiração do documento, fotografia, dentre outras. Essas informações estão disponíveis em duas áreas distintas. A primeira, denominada de Visual Inspection Zone (VIZ), ocupa a maior parte da página a partir do topo é utilizada para a inspeção visual. A outra área, localizada em duas linhas de texto, abaixo da página, é chamada de Machine Readable Zone (MRZ), sendo utilizada para acesso através de dispositivos de leitura ótica (OCR). Figura 7.1: Exemplo de página de dados de um passaporte. Fonte [ICA06] É importante ressaltar que as informações da MRZ também se encontram duplicadas no chip smart card, permitindo a leitura através dos terminais RFID e provendo um elemento adicional de segurança ao documento, uma vez que elas podem ser comparadas pelas autoridades competentes do país receptor. Os detalhes de implementação no smart card, como o tipo de dados e a quantidade de bytes que cada informação ocupa, fazem parte da Logical Data Structure (LDS), detalhada na seção 7.2.2 a seguir. 7.2.2 Estrutura para armazenamento de dados (LDS) De modo a permitir a interoperabilidade na obtenção das informações armazenadas no passaporte eletrônico entre os sistemas de diversos países, especificou-se uma estrutura padronizada para armazenamento, que recebeu o nome de Logical Data Structure (LDS). A LDS é constituída por uma série de 19 grupos de dados (Data Groups (DG)) e por 3 arquivos adicionais contendo informações sobre os Data Groups ou sobre os seus componentes, denominados de Data Elements. Por exemplo, o primeiro data group (DG1) contém as informações da Machine Readable Zone, cada uma armazenada como um data element distinto (tipo 130 do documento, nome do proprietário, número do documento, dentre outros). Os 19 DGs componentes da LDS são: DG1 - Detail(s) recorded in MRZ DG11 - Additional Personal Details DG2 - Encoded face DG12 - Additional Document Details DG3 - Encoded Finger(s) DG13 - Optional Details DG4 - Encoded Eye(s) DG14 - Reserved for Future Use DG5 - Displayed Portrait DG6 - Reserved for future use DG15 - Active Authentication Public Key Information DG7 - Displayed signature or usual mark DG16 - Person(s) to Notify DG8 - Data feature(s) DG17 - Automated Border clearance DG9 - Structure feature(s) DG18 - Electronic Visa(s) DG10 - Substance feature(s) DG19 - Travel Record (s) Desses 19 data groups, apenas o DG1 e o DG2 são obrigatórios. Caso estejam presentes, o armazenamento de impressões digitais é feito no DG3 e as informações colhidas da íris, por sua vez, no DG4. Ressalte-se que os grupos de 17, 18 e 19 são reservados para uso futuro, com a intenção de permitir que a nação que está recebendo um indivíduo possa gravar informações adicionais, tais como o visto e o registro de ocorrências durante a viagem. Observe-se que a versão atual da especificação permite apenas operações de leitura após a emissão do documento. Os arquivos adicionais que fazem parte da estrutura da LDS são os apresentados abaixo. Maiores detalhes acerca desses componentes são fornecidos na parte IV do volume II da especificação, que trata dos protocolos componentes do passaporte. EF.COM - Informação de versão da LDS e uma lista de tags que identifica cada elemento componente da LDS. EF.SOD (Security Object Descriptors) - Armazena informações que permitem a verificação da integridade e autenticidade dos dados armazenados. No primeiro caso, são armazenados os resumos (hashes) de cada data group, digitalmente assinados pela entidade que emitiu o passaporte. Para a verificação da autenticidade o SOD pode conter o certificado digital da entidade emissora. 131 EF.CVCA - Armazena informações para autenticação do terminal (necessário ao protocolo EAC). 7.2.3 Mecanismos de Segurança das Informações Armazenadas Tendo em vista que o chip do passaporte eletrônico armazena informações importantes do seu portador, a especificação [ICA06] define diversos protocolos de segurança que visam restringir o acesso a essas informações e prover mecanismos que assegurem integridade, confidencialidade e autenticidade. Para garantir essas propriedades o conjunto de protocolos utiliza-se de funções de resumo (hashing) e mecanismos de criptografia e de assinatura digital, conforme descrito nesta seção. Dos protocolos apresentados na seção 7.2.4, apenas o Passive Authentication (PA) é de implementação obrigatória de acordo com a especificação. No entanto, os demais protocolos geralmente são utilizados para assegurar a autenticidade do documento, no que diz respeito ao país ou entidade que o emitiu e a legitimidade das informações pessoais armazenadas. Evidenciase que o passaporte brasileiro [Pol10], assim como o modelo europeu, implementa todos os protocolos de segurança descritos na seção 7.2.4. Criptografia e Assinatura digital Criptografia refere-se ao processo de codificar uma mensagem (por exemplo, um documento de texto) em um formato incompreensível, tendo em vista manter o seu conteúdo em sigilo, de modo que alguém que intercepte essa mensagem não possa ter acesso ao seu conteúdo original, a menos que possua uma chave que permita decodificá-la. Denomina-se de cifragem o processo de codificação da mensagem e de decifragem o processo inverso. Os dois modelos clássicos de criptografia são a simétrica e a assimétrica. No primeiro, uma única chave é compartilhada entre o emissor e o receptor da mensagem. Essa chave é utilizada tanto para o procedimento de encriptação quanto para a decifragem da mensagem. Algoritmos simétricos costumam ser bastante eficientes na criptografia de um grande volume de dados, no entanto, um ponto fraco dessa abordagem diz respeito a ser necessária alguma estratégia na distribuição da chave compartilhada. Nesse caso, diversas abordagens podem ser utilizadas, como a que utiliza um algoritmo assimétrico de criptografia, conforme descrito posteriormente na presente seção. Na criptografia assimétrica, um algoritmo é utilizado para emitir duas chaves e, com base nelas, realizar os processos de cifragem e decifragem. Uma delas, a chave pública, pode ser 132 enviada a qualquer pessoa que deseje cifrar uma mensagem que será enviada e decodificada apenas pelo destinatário, utilizando-se a outra chave do par, a chave privada. Nesse caso, podese dizer que há confidencialidade na troca de informações, uma vez que somente o proprietário da chave privada poderá decifrar as mensagens cifradas com a sua chave pública correspondente. Assim, é essencial para preservar o sigilo e a confidencialidade na troca de mensagens que a chave privada seja protegida e mantida em segredo pelo seu proprietário. Ressalta-se que os algoritmos assimétricos são bem mais lentos e requerem maior capacidade de processamento quando comparados aos simétricos. Dessa forma, geralmente essas abordagens são utilizadas em conjunto, aproveitando o que há de mais vantajoso em cada uma. Nessa abordagem, um algoritmo de chave simétrica é utilizado para encriptar os dados (a mensagem) e a chave pública do receptor é utilizada para encriptar a chave simétrica de sessão que encriptou a mensagem. Ao chegar ao receptor, ele usa a chave privada para decriptar a chave de sessão, que por sua vez é usada para decriptar a mensagem. Outra aplicação da criptografia de chave pública é o procedimento denominado de assinatura digital. Nesse caso, não há interesse em preservar o segredo do conteúdo de uma mensagem, mas em assegurar a sua autenticidade, ou seja, em garantir que a mensagem de fato pertence a um determinado emissor. Nesse caso, é feito o processo inverso ao descrito anteriormente. A mensagem é cifrada com a chave privada do emissor e decodificada, no receptor, através da chave pública correspondente. Caso a decifragem tenha sido bem sucedida, então é possível concluir que a chave privada do emissor foi utilizada na encriptação, e confiando-se que o emissor é o único detentor dessa chave, conclui-se que ele enviou a mensagem. Resumo criptográfico (hashing) Por questões de desempenho relacionadas aos algoritmos assimétricos e para assegurar a integridade dos dados, na assinatura digital normalmente a mensagem cifrada não é o arquivo ou documento a ser “assinado”, mas um resumo desse. O resumo (hash) é gerado através de uma função de hash que recebe como entrada uma sequência de bytes de tamanho variável (a mensagem) e retorna um número de tamanho fixo entre 16 e 20 bytes, a depender do algoritmo utilizado. Além de ser de tamanho pequeno, o resumo também auxilia a garantir a integridade da informação enviada, uma vez que ele é um valor único para um determinado arquivo. Caso o documento enviado seja modificado antes de chegar ao receptor, ao se gerar novamente o seu resumo, ele será diferente do que foi gerado anteriormente. Assim, na assinatura digital, ao receptor é entregue o arquivo de dados e, junto com ele, o seu resumo criptografado. Através da chave pública, o resumo enviado é decodificado e comparado com o resumo gerado localmente. 133 Se eles forem iguais, então pode-se dizer que o arquivo pertence ao emissor e não foi adulterado. Certificação Digital O certificado digital é uma solução para a distribuição da chave pública em um sistema de autenticação digital. A função do certificado é associar uma pessoa ou entidade a uma chave pública específica. A emissão de um certificado é feita por uma autoridade certificadora (AC) que assina digitalmente o certificado, dessa forma permitindo alguém interessado em utilizálo verificar a sua autenticidade e integridade. Assim, o usuário do certificado deve confiar na idoneidade da autoridade certificadora. As entidades certificadoras são os principais componentes de um sistema de emissão, distribuição, armazenamento e revogação de certificados, denominado de infra-estrutura de chaves públicas (ICP, ou PKI, no inglês). No Brasil, há diversas ACs, como a Caixa Econômica Federal [CEF11] e o Certisign [Cer11], todas subordinadas ao Instituto Nacional de Tecnologia da Informação (ITI) [ITI11], que vem a ser a Autoridade Certificadora Raiz Brasileira e ao Comitê Gestor da ICP-Brasil. PKI no passaporte eletrônico No sistema de passaporte eletrônico, cada Estado é independente para gerar as chaves que serão utilizadas na assinatura digital de certificados. A proteção de chaves privadas é de responsabilidade da nação que as gerou, e as chaves públicas devem ser distribuídas entre todos os Estados que utilizam o sistema de passaporte eletrônico. Na hierarquia de certificação, o certificado de mais alto nível é o distribuído pela entidade certificadora de um país, o Country Signing Certification Authority Certificate (CCSCA ). A chave privada do CCSCA é utilizada para assinar Document Signer Certificates (CDS ). Por sua vez, a chave privada do Document Signer Certificate (KPrDS ) é utilizada para assinar os Document Security Objects (SOD ) que fazem parte da estrutura de dados (LDS) de cada passaporte emitido. A ICAO discponibiliza um diretório de chaves públicas (PKD) que armazena Document Signer Certificates utilizados por todos os Estados participantes, incluindo aqueles que não estão mais em uso, mas que foram utilizados para assinar passaportes emitidos no passado. Esse PKD é o principal mecanismo global de distribuição dos CDS , sendo acessível por todos os países participantes. Observa-se que a ICAO não distribui os CCSCA , mas os utiliza para validar os CDS enviados. 134 7.2.4 Protocolos de Segurança A presente seção fornece uma visão geral dos protocolos que servem ao propósito de autenticação e de restrição do acesso aos dados do passaporte apenas a terminais autorizados. Em maior destaque encontram-se os protocolos Passive Authentication (PA) e Basic Access Control (BAC), que são os dois protocolos tratados na versão atual da especificação apresentada neste trabalho. Os demais serão acrescentados em uma versão futura do desenvolvimento. PA (Passive Authentication) O mecanismo de Passive Autentication é utilizado para verificar se o conteúdo do Document Security Object (SOD ) e da estrutura de dados LDS são autênticos e não foram modificados. Para tanto, a autoridade interessada em inspecionar o documento, de posse do Document Signer Certificate armazenado no chip, ou apenas da sua chave pública, pode verificar a autenticidade do Document Security Object (SOD ) e, consequentemente, da LDS. Ressalte-se que a Passive Authentication é o único tipo de autenticação obrigatória recomendada pela ICAO. No entanto, ela não é capaz de assegurar a autenticidade do chip, ou mesmo se o conteúdo do chip é uma cópia exata de outro. Ela também não previne a leitura não autorizada das informações armazenadas (skimming) e a escuta da comunicação entre terminais e a aplicação cliente (eavesdropping). Assim, outros mecanismos de segurança são geralmente adotados pelas autoridades emissoras do passaporte eletrônico. O processo de Passive Authentication é composto pelos seguintes passos: 1. Leitura do Document Security Object (SOD ). O SOD pode opcionalmente conter o Document Signer Certificate (CDS ). 2. Leitura do Document Signer Certificate (CDS ). 3. Verificação do SOD através da chave pública (KPuDS ) do CDS . 4. Verificação do CDS utilizando a chave pública (KPuCSCA ) do CSCA. 5. Leitura dos data groups da LDS. 6. Cálculo dos hashes dos data groups. 7. Comparação dos hashes calculados com aqueles armazenados no SOD 135 Caso as verificações feitas nos passos 3 e 4 estejam corretas, significa que o conteúdo do SOD é confiável. Se o passo 7 for bem-sucedido assegura-se que os conteúdos dos data groups não foram modificados. BAC (Basic Access Control) O Basic Access Control (BAC) é o protocolo inicial para permitir a comunicação segura entre a aplicação host e o chip. Uma implementação de passaporte que suporta o BAC nega o acesso ao conteúdo do chip até que o sistema de inspeção (terminal de leitura) prove que está autorizado a obter esse conteúdo. O uso do BAC previne o acesso não autorizado ao chip, evitando duas violações de segurança, são elas, o skimming e o eavesdropping. A primeira é a capacidade de leitura dos dados contidos no chip por meio eletrônico sem a necessidade de abrir o documento, ou mesmo tê-lo em mãos. Já a segunda diz respeito a escuta de uma comunicação não-criptografada entre a aplicação terminal e o applet dentro de uma distância de alguns metros. O protocolo BAC evita o skimming, pois necessita obrigatoriamente da leitura (ótica ou visual) dos dados contidos na Machine Readable Zone (MRZ) do passaporte. Nesse caso, o passaporte tem que ser aberto por um agente autorizado para que a leitura dos dados possa ser feita, contando com a conivência e a confiança do portador do passaporte. No caso das escutas (eavesdropping), a sua prevenção é garantida através da criptografia do canal de comunicação entre a aplicação host e o chip, utilizando o mecanismo de secure messaging, que é estabelecida após a autenticação através do BAC, utilizando as chaves de sessão SKENC e SKMAC . O protocolo BAC pode ser resumido a partir da execução das seguintes etapas: 1. O sistema de inspeção lê as informações do MRZ utilizando meio ótico de armazenamento. Caso não haja dispositivo ótico de leitura, é possível ler as informações diretamente da página de dados e introduzí-las manualmente, por meio de um teclado. O número do documento, a data de nascimento do portador, e data de expiração do passaporte, bem como seus dígitos verificadores, são utilizados como base para gerar uma Key seed (Ks ). 2. A Ks é formada pelos 16 bytes mais significativos do hash SHA-1 das informações do MRZ e é utilizada para derivar as Document Basic Access Keys, denominadas de KENC e KMAC . Essas chaves serão utilizadas, respectivamente, para criptografia e verificação de integridade das mensagens trocadas durante o processo de autenticação mútua. 136 3. O sistema de inspeção e o chip autenticam-se mutuamente (detalhes na seção 7.3.2) e derivam chaves de sessão SKENC e SKMAC para proteção de criptografia e integridade na sessão de comunicação após o BAC. Um número de sequência (SSC) também é calculado e compartilhado entre as duas aplicações. 4. Após a autenticação bem-sucedida, toda comunicação subsequente deve ser encriptada por Secure Messaging utilizando a chave SKENC . Observa-se que em cada operação entre o terminal e o cartão o número de sequência SSC gerado após o BAC é incrementado. AA (Active Authentication) O protocolo Active Authentication (AA) tem por objetivo assegurar a autenticidade do chip e das informações armazenadas no Document Security Object (SOD ). De outra forma, a verificação bem-sucedida com o AA garante que o chip e seus dados não foram clonados. O chip deve possuir uma chave pública (KPuAA ) e uma chave privada (KPrAA ) específicas para Active Authentication. A chave pública para AA (KPuAA ) é inserida no data group 15 da LDS. A representação hash desse data group é autenticada pela assinatura digital do emissor do passaporte, sendo armazenada no (SOD ). Por sua vez, a chave privada é armazenada de forma protegida na memória do chip smart card. O mecanismo básico de autenticação do cartão perante o terminal consiste nos seguintes passos: 1. Envio, pelo terminal, de uma mensagem (de tamanho recomendado de 8 bytes) para o cartão. 2. A mensagem é recebida pelo cartão, assinada digitalmente com a chave privada KPrAA e devolvida ao cliente. 3. O cliente então verifica autenticidade da mensagem a partir da chave pública KPuAA , obtida a partir da leitura do DG-15. EAC (Extended Access Control) Os meios biométricos de identificação por meio de imagens da impressão digital e da íris podem opcionalmente ser utilizados pelos Estados como forma de aumentar o rigor na identificação. Por se tratarem de meios mais invasivos à privacidade, eles devem possuir maior proteção, o que pode ser feito limitando-se o acesso aos dados ou criptografando-os. 137 No caso da restrição ao acesso, recomenda-se a utilização do protocolo Extended Access Control. A documentação oficial do passaporte eletrônico não fornece detalhes sobre o EAC. No entanto, nas implementações atuais do passaporte eletrônico, como os passaportes europeus, e o passaporte brasileiro, esse protocolo vem sendo utilizado para a proteção dos dados biométricos adicionais. O EAC é dividido em duas partes, ou dois subprotocolos, o CA (Chip Authentication) e o TA (Terminal Authentication). O primeiro é semelhante ao AA (Active Authentication), servindo ao propósito de autenticar o cartão perante o terminal cliente. No caso do TA, o terminal é quem irá autenticar-se perante o cartão, demonstrando que pode ter acesso aos dados biométricos adicionais. 7.3 Especificação inicial dos serviços do Passaporte Nesta seção é descrita a especificação textual dos serviços do passaporte eletrônico que foram desenvolvidos neste estudo. Essa abordagem inicial tem por objetivo descrever os requisitos funcionais e as restrições impostas por esses serviços de forma a facilitar a compreensão da sua especificação formal em B, composta por diversos máquinas e refinamentos, em um desenvolvimento seguindo o método BSmart. É importante salientar que são especificados apenas os serviços fornecidos pela aplicação cartão (applet), uma vez que essa aplicação é o foco principal do método BSmart, juntamente com a geração da interface de comunicação dos serviços do applet fornecidos à aplicação cliente. No entanto, observa-se que parte do serviço global da aplicação é processada pela aplicação cliente, utilizando-se de APIs de terceiros para permitir, por exemplo, cifragem/decifragem de informações, a geração de funções de resumo (hash) e a criação de chaves de segurança, conforme requerido pelos protocolos da aplicação código-aberto JMRTD. Devido à complexidade em se lidar com uma grande quantidade de código Java de terceiros, o processamento na parte cliente não é tratado na versão atual do desenvolvimento. Neste trabalho, especificou-se os serviços presentes no cartão requeridos para o funcionamento dos protocolos PA (Passive Authentication) e BAC (Basic Access Control). Esses serviços relacionam-se à requisição de arquivos armazenados na estrutura de dados lógica (LDS) do applet e aos procedimentos de autenticação e geração de chaves de segurança que protegem a comunicação entre as aplicações cliente e terminal. Os demais serviços que fornecem suporte aos outros protocolos de segurança AA e EAC serão acrescentados em uma versão futura deste desenvolvimento. 138 7.3.1 Serviço de geração de número randômico Descrição: Possibilita a geração de um número randômico (challenge) que é utilizado pelo protocolo Basic Access Control para iniciar o processo de autenticação mútua entre o applet e a aplicação host. O número gerado deve ser armazenado internamente pelo applet e enviado ao cliente. Pré-requisitos: 1. O BAC não deve ter sido completado ainda, uma vez que esse protocolo necessita do número randômico gerado pelo presente serviço. Fluxo principal: 1. O cliente requisita a geração do número randômico. 2. O número, composto por 8 bytes, é gerado e armazenado internamente no applet para posterior uso. 3. O número é retornado para o cliente. 7.3.2 Serviço de autenticação mútua (cliente-cartão) Descrição: A autenticação mútua é parte do protocolo BAC, sendo utilizada para o estabele- cimento de chaves de encriptação (SKENC ) e de resumo (SKMAC ). Através do uso dessas chaves é possível, após o BAC, utilizar o mecanismo de secure messaging, que consiste em realizar a encriptação do canal de comunicação entre as aplicações host e cartão (SKENC ) e assegurar a integridade das mensagens trocadas entre elas (SKMAC ). Pré-requisitos: 1. Devem ter sido geradas previamente as chaves KENC e KMAC , derivadas a partir das informações da Machine Readable Zone (MRZ). 2. O serviço de geração de número randômico deve ter sido requisitado. 3. O processo de autenticação mútua não deve ter sido realizado previamente. 139 Fluxo principal: 1. Terminal (IFD) (a) Após requisitar o número randômico (RNDICC ) ao cartão e recebê-lo, o terminal deve gerar outro número randômico (RNDIFD ); (b) Uma chave KIFD (16 bytes) deve ser gerada a partir de RNDIFD . As chave KIFD e KICC (gerada pelo cliente) serão utilizadas para derivar as chaves de seção KSENC e KSMAC ; (c) Deve ser feita a concatenação (símbolo “||”) dos números randômicos e a chave (S = RNDIFD || RNDICC || KIFD ); (d) S deve ser encriptado utilizando a chave KENC (EIFD = enc[KENC ](S)); (e) Deve ser gerado o resumo do criptograma EIFD (MIFD = mac[KMAC ] (EIFD )); (f) O terminal envia ao cartão um comando MUTUAL_AUTHENTICATE contendo EIFD ||MIFD . 2. Cartão (ICC) - parte especificada neste trabalho (a) Verifica-se o resumo (MIFD ) do criptograma EIFD ; (b) Decripta-se EIFD ; (c) Deve-se extrair RNDICC de S e verificar se o terminal retornou o valor correto, comparando com o valor gerado e que foi enviado ao terminal anteriormente; (d) A chave KICC (16 bytes) deve ser gerada a partir de RNDICC ; (e) Os números randômicos e a chave devem ser concatenados (R = RNDICC || RNDIFD || KICC ); (f) R deve ser encriptado utilizando a chave KENC (EICC = enc[KENC ](R)); (g) Deve-se computar o resumo do criptograma EICC (MICC = mac[KMAC ] (EICC )); (h) O cartão deve enviar a resposta ao terminal contendo EICC ||MICC . 3. Terminal (a) Deve-se verificar o resumo (MICC ) do criptograma EICC (b) O terminal decripta EICC ; (c) O terminal extrai RNDIFD de R e verifica se foi retornado o valor correto (o mesmo valor que foi enviado ao cartão). 140 Fluxo alternativo: 1. fluxo principal, item 2. Em caso de falha em qualquer etapa, deve-se retornar um código de erro para o terminal. 7.3.3 Serviço de leitura de arquivos Descrição: O serviço de leitura de arquivos possibilita que o cliente obtenha um determinado arquivo armazenado na estrutura de dados lógica (LDS). Pré-requisitos: 1. O cliente deve estar autenticado para que possa obter acesso aos arquivos. Neste caso, a BAC deve ter sido feita previamente. 2. O cliente deve fornecer um identificador de arquivo válido. Fluxo principal: 1. O cliente devidamente autenticado requisita um arquivo através do identificador do arquivo. 2. Caso o identificador seja válido o arquivo é retornado para o cliente. Fluxo alternativo: 1. fluxo principal, item 2. No caso do identificador do arquivo não ser válido, nenhum arquivo deve ser retornado. Ao invés disso, um código de erro deve ser enviado para o cliente. 7.4 Especificação Formal Abstrata Na presente seção detalha-se a especificação abstrata da aplicação de passaporte eletrônico JMRTD. Ela é a base para a criação da especificação Java Card que será inserida no cartão (seção 7.5), assim como é a partir da interface das operações deste módulo que é traduzida, de forma automatizada, a API para a aplicação host. Este e outros módulos importantes que compõem o desenvolvimento do passaporte podem ser consultados em detalhes no apêndice C. 141 As máquinas referenciadas e a definição dos componentes de estado da especificação são apresentados na figura 7.2. Na cláusula SEES são inseridas as especificações dos tipos primitivos de Java do KitSmart (JByte, JShort e JBoolean) e máquinas de contexto (Passport_Context e FileSystem_Context) que incluem constantes que são utilizadas na especificação abstrata e em seus refinamentos. Observa-se que a única máquina inclusa na especificação abstrata é a máquina FileSystem, que modela, através da operação getFile, a obtenção de um arquivo da estrutura de dados LDS do passaporte. Tendo em vista simplificar o modelo decidiu-se uniformizar a representação dos arquivos como uma sequência de 8 bytes. MACHINE Passport SEES JByte, JShort, JBoolean, Passport_Context, FileSystem_Context INCLUDES fileSystem.FileSystem VARIABLES perState, volState, selectedFile, rnd, ssc INVARIANT perState ∈ JBYTE ∧ volState ∈ seq1(JBYTE) ∧ size (volState) = 1 ∧ selectedFile ∈ EF_DG1_INDEX .. SOS_LOG_INDEX ∧ rnd ∈ seq1 (JBYTE) ∧ ssc ∈ seq1 (JBYTE) INITIALISATION perState := PER_STATE_INIT || volState := [NO_FILE_SELECTED] || selectedFile := EF_DG1_INDEX || rnd := [0, 0, 0, 0, 0, 0, 0, 0] || ssc := [0, 0, 0, 0, 0, 0, 0, 0] OPERATIONS (...) Figura 7.2: Máquina Passport - máquinas referenciadas e variáveis de estado. Em relação às variáveis de estado, cumpre observar que a variável perState representa o estado atual da aplicação que deve ser armazenado no cartão de forma persistente, ou seja, que deve ser mantido entre diferentes sessões de comunicação. Por outro lado, volState armazena o estado que pode ser modificado a cada sessão de uso da aplicação, mas não necessita ser preservado entre elas. A variável selectedFile armazena o índice do último arquivo selecionado, que pode ser um entre dezenove índices de arquivos. Por sua vez, o componente do estado rnd guarda o número randômico gerado após a chamada à operação getChallenge. Por fim, a variável ssc é o contador de sessão, inicializado com o estabelecimento do BAC e incrementado a cada nova sessão de comunicação entre o terminal e o cartão. O contador de seção, na especificação oficial, é um valor armazenado em um array de bytes. No desenvolvimento Passport 142 essa representação foi preservada ao tipá-lo como uma sequência de bytes. Ressalta-se que no código Java Card da aplicação de passaporte eletrônico, o estado persistente é inicializado simplesmente com o valor 0 (zero). Na especificação B, criou-se o estado PER_STATE_INIT para representar essa situação inicial. Apenas para deixá-la em um estado consistente, a variável selectedFile recebeu o valor de um dos arquivos do passaporte (EF_DG1_INDEX). A efetiva indicação de que um arquivo está autorizado para ser requisitado depende do estado da variável volState. Na situação inicial, ela é inicializada com NO_FILE_SELECTED, indicando que nenhum arquivo foi requisitado ainda para seleção. Por fim, as sequências de byte rnd e ssc tem cada byte inicializado com o valor zero (0), uma vez que elas recebem valores significativos apenas através da chamada às operações getChallenge e processMutualAuthenticate, respectivamente. Com respeito às operações, inicialmente destaca-se a operação processGetChallenge (figura 7.3), utilizada na geração de um número randômico (challenge), requisito essencial para o processo de autenticação do protocolo BAC, conforme descrito na seção 7.3.1. Ela recebe dois parâmetros, a saber, protectedAPDU, indicando se a comunicação entre as aplicações deve ser protegida e le, que se refere ao tamanho esperado da resposta, nesse caso, do número randômico a ser gerado (normalmente, 8 bytes). A operação tem como pré-condições que as chaves iniciais de autenticação mútua tenham sido geradas ( perState = HAS_MUTUAL_AUTHENTICATION_KEYS) e que o processo de autenticação não tenha sido previamente executado (¬ volState (1) = MUTUAL_AUTHENTICATED). A operação deve armazenar internamente o número randômico gerado como uma sequência de bytes (rnd :∈ number) e retorná-lo ao cliente (rnd_val :∈ number). rnd_val ← processGetChallenge (protectedAPDU, le) = PRE protectedAPDU ∈ JBOOLEAN ∧ le ∈ JSHORT ∧ perState = HAS_MUTUAL_AUTHENTICATION_KEYS ∧ ¬ (volState(1) = MUTUAL_AUTHENTICATED) THEN volState(1) := CHALLENGED || ANY number WHERE number = seq1(JBYTE) THEN rnd :∈ number || rnd_val :∈ number END END; Figura 7.3: Máquina Passport - operação processGetChallenge. Na aplicação cartão, a autenticação através do protocolo BAC (descrita na seção 7.3.2) é 143 finalizada através da chamada à operação processMutualAuthenticate (figura 7.4). Para tanto, a operação getChallenge deve ter sido previamente invocada (volState(1) = CHALLENGED) e o processo de autenticação ainda não deve ter sido requisitado para a seção de comunicação em curso (¬(volState (1) = MUTUAL_AUTHENTICATED)). A pós-condição deve levar a especificação para o estado MUTUAL_AUTHENTICATED e os bytes correspondentes aos números randômicos (RNDICC e RNDIFD ) e à chave de autenticação KICC devem ser gerados e retornados à aplicação host através da variável key_data. key_data← processMutualAuthenticate (protectedAPDU)= PRE protectedAPDU ∈ JBOOLEAN ∧ (volState(1) = CHALLENGED ∧ ¬ (volState(1) = MUTUAL_AUTHENTICATED)) THEN volState(1) := MUTUAL_AUTHENTICATED || ssc :∈ seq1(JBYTE) || key_data :∈ seq1(JBYTE) END; Figura 7.4: Máquina Passport - operação processMutualAuthenticate. As operações processSelectFile (figura 7.5) e processReadBinary (figura 7.6) especificam, respectivamente, a seleção e o retorno de um arquivo para o terminal de consulta. A primeira recebe um número (fid) representando um índice de arquivo, que deve estar dentro da faixa permitida (fid ∈ EF_DG1_INDEX .. SOS_LOG_INDEX). processSelectFile (fid)= PRE fid ∈ EF_DG1_INDEX .. SOS_LOG_INDEX ∧ volState(1) = MUTUAL_AUTHENTICATED ∧ ¬ (perState = LOCKED) THEN CHOICE selectedFile := fid || volState(1) := FILE_SELECTED OR volState(1) := NO_FILE_SELECTED END END; Figura 7.5: Máquina Passport - operação selectFile. As demais pré-condições que devem ser obedecidas para a correta aplicação da operação processSelectFile dizem respeito aos estados volátil e persistente. A primeira delas (volState(1) = MUTUAL_AUTHENTICATED) indica que é preciso que a autenticação entre o terminal e o cartão tenha sido estabelecida, o que é feito através da chamada às operações getChallenge e processMutualAuthenticate. Em relação ao estado persistente, o cartão não deve estar no estado bloqueado (perState = LOCKED). No corpo da operação, em uma aplicação bem sucedida, o 144 índice de arquivo é salvo na variável de estado selectedFile e o estado volátil é modificado para indicar que um arquivo foi selecionado (FILE_SELECTED). Caso contrário, o estado é atualizado para indicar que nenhum arquivo foi selecionado. Estando o canal de comunicação segura entre o cliente e o cartão devidamente estabelecido e um identificador de arquivo selecionado (processSelectFile), a aplicação terminal pode requisitar o arquivo correspondente ao passaporte. O envio efetivo de um arquivo é feito através da operação processReadBinary (Figura 7.6), que obtém o arquivo por meio da operação getFile da máquina inclusa FileSystem, passando para ela o identificador armazenado na variável de estado selectedFile. file← processReadBinary (protectedAPDU, le)= PRE protectedAPDU ∈ JBOOLEAN ∧ le ∈ JSHORT ∧ volState(1) = FILE_SELECTED THEN file ← fileSystem.getFile(selectedFile) END; Figura 7.6: Máquina Passport - operação processReadBinary. 7.5 Versão Java Card da Máquina Passport A segunda etapa do processo formal compreende o desenvolvimento Java Card da especificação (figura 7.7). Nessa versão, modificou-se a interface das operações para informar a recepção dos parâmetros e o envio dos resultados através de um array de bytes, dessa forma tornando o modelo compatível à interface padrão de uma aplicação Java Card, na qual a comunicação entre as aplicações é feita através do buffer apdu. MACHINE Passport_JC (. . . ) OPERATIONS processSelectFile (apdu_p, data_in) = (. . . ) data_out ← processReadBinary (apdu_p, protectedAPDU, data_in) = (. . . ) data_out ← processGetChallenge (apdu_p, protectedAPDU, data_in) = (. . . ) data_out ← processMutualAuthenticate (apdu_p, protectedAPDU) = (. . . ) END Figura 7.7: Interface das operações da máquina Passport_JC. Observa-se que os dados inseridos no campo data do buffer são recebidos no parâmetro data_in e que os dados enviados na resposta ao cliente devem ser inseridos no array data_out. Acrescentou-se à interface de todas as operações concretas o parâmetro apdu_p, que modela 145 a recepção de um objeto APDU, tornando possível o acesso a todas as operações da máquina Apdu. Em relação ao invariante, a especificação dos componentes de estado não foi alterada, tendo em vista que os tipos de dados utilizados na especificação inicial são compatíveis com a especificação cartão (byte, short, boolean e array de byte). A verificação da conformidade entre este modelo concreto e a especificação abstrata é demonstrada na seção 7.8. Assim, mesmo possuindo interfaces distintas, é possível provar uma noção de conformidade através de funções que verificam a compatibilidade entre os componentes da interface abstrata e as sequências de bytes data_in e data_out presentes na assinatura do módulo Java Card. Dessa forma, torna-se possível que o desenvolvimento Java Card prossiga até o refinamento concreto de implementação, como se fosse um refinamento tradicional do componente abstrato Passport. 7.6 Refinamento Full Function O refinamento full function da máquina Passport_JC segue o modelo definido na seção 4.3.1 e foi traduzido com o auxílio da ferramenta de geração de refinamento full function do conjunto de ferramentas de suporte ao método BSmart. O módulo gerado foi verificado correto quanto à sintaxe, tipos e obrigações de prova. REFINEMENT Passport_JC_FF_ref REFINES Passport_JC SEES JByte, JShort, JBoolean, ISO7816, APDU_Properties, Passport_Context, FileSystem_Context, Passport_JC_FF_Context INCLUDES fileSystem.FileSystem, ISOException.ISOException Figura 7.8: Máquinas referenciadas pelo refinamento Full Function. Observa-se pela figura 7.8 que o refinamento referencia, na cláusula SEES, a máquina de contexto Passport_JC_FF_Context (figura 7.9), na qual estão definidas as constantes para representar os códigos de exceção identificados nas operações, bem como constantes que definem o código de instrução (INS) de cada serviço do cartão. Também destaca-se a inclusão (cláusula INCLUDES) da máquina ISOException, que modela a chamada à operação throwIt para o lançamento de exceções em Java Card. Conforme explanado na seção 4.3.1, o modelo full function das operações consiste em especificar as restrições da pré-condição abstrata na forma de substituições condicionais, dessa 146 MACHINE Passport_JC_FF_Context SEES JByte, JShort CONSTANTS ex_processGetChallenge0, ex_processGetChallenge1, (. . . ), processMutualAuthenticate_INS PROPERTIES ex_processGetChallenge0 ∈ JSHORT ∧ ex_processGetChallenge1 ∈ JSHORT ∧ ex_processSelectFile0 ∈ JSHORT ∧ (. . . ) processReadBinary_INS ∈ JSHORT ∧ processMutualAuthenticate_INS ∈ JSHORT END Figura 7.9: Máquina de contexto Passport_JC_FF_Context. forma permitindo que elas se tornem explícitas no código Java Card a ser gerado. Desse modo, a funcionalidade da operação só será efetivamente aplicada se todas as restrições forem obedecidas, caso contrário um código de erro é retornado ao cliente, seguindo o modelo de exceção em Java Card. A figura 7.10 apresenta a versão full function da operação processSelectFile. As précondições que restringem a correta aplicação da operação aos casos em que a autenticação mútua foi realizada e que o cartão não está bloqueado são inseridas no corpo da operação. As restrições apenas de tipagem são suprimidas da pré-condição, uma vez que já são declaradas no módulo abstrato. processSelectFile (apdu_p, data_in) = PRE apdu_p ∈ APDU ∧ data_in ∈ seq1(JBYTE) ∧ data_in(1) ∈ EF_DG1_INDEX .. SOS_LOG_INDEX ∧ jc_volState(1) = MUTUAL_AUTHENTICATED ∧ ¬ (jc_perState = LOCKED) THEN CHOICE jc_selectedFile := data_in(1) || jc_volState(1) := FILE_SELECTED OR jc_volState(1) := NO_FILE_SELECTED END END; processSelectFile (apdu_p, data_in) = IF jc_perState = LOCKED THEN ISOException.throwIt(ex_processSelectFile0) ELSIF ¬ (jc_volState(1) = MUTUAL_AUTHENTICATED) THEN ISOException.throwIt(ex_processSelectFile1) ELSE CHOICE jc_selectedFile := data_in(1) || jc_volState(1) := FILE_SELECTED OR jc_volState(1) := NO_FILE_SELECTED END END; Figura 7.10: Versão full function (direita) da operação processSelectFile. Observa-se que o corpo da operação é aplicado (após o else do primeiro condicional) caso as restrições sejam obedecidas. Nesse caso, inicialmente especifica-se o recebimento do identificador do arquivo através do primeiro byte da sequência data_in (data_in(1)). O restante da operação é semelhante ao modelo abstrato, tendo dois comportamentos possíveis, a saber, atribuir o identificador do arquivo a selectedfile e modificar o estado volátil para indicar a seleção do arquivo, ou não selecionar nenhum arquivo, caso o identificador seja inválido. 147 7.7 Implementação B do Passaporte A implementação do passaporte é a ultima etapa do desenvolvimento em B. Ela é dividida em duas partes, a saber, (i) implementação em B do desenvolvimento Java Card que especifica os serviços do passaporte eletrônico e (ii) implementação de um módulo JC_Applet que refina a máquina Applet da API Java Card, sendo portanto o modelo para a classe applet que será gerada. Essa implementação importa o desenvolvimento Passport_JC e insere o código de requisição de serviços em seu método process. Observa-se que é necessário implementar todas as dependências das implementações principais que necessitem ter seu código Java Card gerado. 7.7.1 Máquinas externas e estado da implementação A figura 7.11 apresenta uma visão geral das máquinas que compõem a implementação e as suas principais dependências. Figura 7.11: Visão geral dos módulos principais do desenvolvimento Passport. O desenvolvimento dos serviços oferecidos pela aplicação é apresentado na parte central da figura (Passport_JC, Passport_JC_FF_ref, Passport_JC_imp). A implementação importa (cláusula IMPORTS) diversas máquinas que auxiliam na implementação dos serviços. As máquinas FileSystem, FileIndex, KeyStore, PassportCrypto e PassportUtil e suas implementações foram derivadas de classes correspondentes da aplicação de código-aberto JMRTD. A seguir, descreve-se de forma resumida cada um desses componentes. FileIndex e FileSystem FileIndex contém uma operação getFileIndex que, dado um identificador de arquivo, retorna o seu índice correspondente. O módulo FileSystem, por sua vez, possui uma operação que modela o retorno de um arquivo, dado o seu identificador. KeyStore Possui operações para armazenar e retornar as chaves KENC e KMAC necessárias ao BAC. 148 PassportCrypto Contém diversos serviços úteis ao BAC e a outros protocolos do passaporte, permitindo encriptar, decriptar, gerar resumo criptográfico, verificar resumo, gerar chaves, e computar o contador de sessão. PassportUtil implementa serviços diversos, que possibilitam, por exemplo, a troca dos valores armazenados em duas sequências de bytes e o retorno do menor entre dois valores. É utilizado pelo serviço processMutualAuthenticate. É importante destacar outra categoria de máquinas importadas, as máquinas de biblioteca, utilizadas para encapsular o estado da implementação. Na figura, destacam-se as máquinas que modelam sequência (LM_SEQ_x), cujas implementações variam conforme o tipo de dados da sequência ou restrições impostas (número de elementos, tipo dos elementos, etc.). As máquinas de sequência refinam as variáveis volState (estado volátil), rnd (número randômico) e ssc (contador de sessão). Por sua vez, a máquina LM_BYTE encapsula o estado de 1 byte, refinando perState. Por fim, a biblioteca LM_SHORT_FILE é utilizada para armazenar o conteúdo da variável selectedFile, um valor short restrito a um índice de arquivo. A figura 7.12 exibe as máquinas vistas, importadas e o invariante da implementação, que liga o estado abstrato às variáveis correspondentes das máquinas de biblioteca. Observa-se que todas as máquinas importadas devem receber um prefixo (nome de instância) que, no código Java Card gerado, será o nome do objeto Java criado. IMPLEMENTATION Passport_JC_imp REFINES Passport_JC_FF_ref SEES JByte, JShort, JBoolean, APDU_Properties, ISO7816, Passport_Context, FileSystem_Context, Passport_JC_FF_Context IMPORTS per_state.LM_BYTE, sel_file.LM_SHORT_FILE, vol_state.LM_SEQ_0, jc_rnd_var.LM_SEQ_1, jc_ssc_var.LM_SEQ_1, fileSystem.FileSystem, passportUtil.PassportUtil, crypto.PassportCrypto, keyStore.KeyStore, ISOException.ISOException, randomData.RandomData, apdu.Apdu, Util.Util INVARIANT jc_perState = per_state.num_byte ∧ jc_volState = vol_state.bt_seq ∧ jc_selectedFile = sel_file.num_short ∧ jc_rnd = jc_rnd_var.bt_seq ∧ jc_ssc = jc_ssc_var.bt_seq Figura 7.12: Máquinas referenciadas e especificação do estado da implementação Passport_JC_imp. No caso das máquinas da API Java Card do KitSmart, utilizou-se as especificações ISOException, APDU, Util e RandomData, assim como foi feito para o refinamento Passport_JC_FF_ref. O uso dessas máquinas permite a verificação do uso correto das operações quanto aos tipos de dados utilizados, restrições aos valores desses tipos e quanto à necessidade 149 da aplicação prévia de outras operações, como no caso das operações de recepção e envio de dados da máquina Apdu. Dessa forma, ao ser gerado o código Java Card, os métodos serão chamados com o tipos corretos e na ordem adequada. O invariante da implementação relaciona as variáveis abstratas com o estado das máquinas de biblioteca. Por exemplo, jc_perState = per_state.num_byte liga a variável de estado jc_perState à variável num_byte da máquina LM_BYTE. Assim, toda operação sobre jc_perState será efetuada por meio das operações da máquina LM_BYTE, atualizando ou consultando a variável num_byte. De forma semelhante, o mesmo é feito para os outros componentes do estado da especificação Passport_JC_imp. A seção 7.7.2 a seguir apresenta o refinamento concreto das operações processGetChallenge e processSelectFile. 7.7.2 Operações processGetChallenge e processSelectFile Nesta seção apresenta-se a implementação das operações processGetChallenge e processSelectFile. Algumas partes dos códigos foram omitidas para deixá-los menos extensos e para que a explicação concentre-se nos aspectos mais relevantes. data_out ← processGetChallenge (apdu_p, protectedAPDU, data_in) = VAR le_val,v_st, per_st, bf, bf_offset, jc_rnd, apdu_state, out IN bf ← apdu.getBuffer; v_st ← vol_state.getFirst; per_st ← per_state.get_num; jc_rnd ← jc_rnd_var.getSeq; IF v_st = MUTUAL_AUTHENTICATED THEN ISOException.throwIt(ex_processGetChallenge0) ELSIF ¬ (per_st = HAS_MUTUAL_AUTHENTICATION_KEYS) THEN ISOException.throwIt(ex_processGetChallenge1) ELSE (...) le_val ← Util.getShort(data_in, cast_short (0)) (...) randomData.generateData(jc_rnd, 0, le_val); //Geração do número randômico (...) IF sum_short(cast_short(0), le_val) ≤ size(jc_rnd) ∧ sum_short(cast_short(0), le_val) ≤ size(bf) ∧ bf_offset ≥ 0 ∧ bf 6= [] ∧ sum_short(bf_offset, le_val) ≤ size(bf) THEN out ← Util.arrayCopyNonAtomic(jc_rnd, cast_short(0), bf, bf_offset, le_val); jc_rnd_var.setSeq (jc_rnd) //Atualização da variável de estado ELSE ISOException.throwIt(SW_INTERNAL_ERROR) END; vol_state.set(CHALLENGED); //Atualização do estado da aplicação data_out := jc_rnd //Retorno do número gerado END END; Figura 7.13: Passport_JC_imp - operação concreta processGetChallenge. Na operação processGetChallenge (figura 7.13), após a verificação das restrições de estado inclusas no modelo full function, vê-se a obtenção do valor tamanho esperado da resposta (le) diretamente do array data_in através da operação getShort da máquina Util da api Java Card (le_val ← Util.getShort(data_in, cast_short (0))). Posteriormente, a sequência aleatória de 8 150 bytes é gerada e armazenada na variável local jc_rnd, com o auxílio da operação generateData da máquina RandomData (randomData.generateData (jc_rnd, 0, le_val)). O valor local é então utilizado na atualização do estado global (jc_rnd_var.setSeq (jc_rnd)). Observa-se que a cópia da variável jc_rnd no buffer apdu é o primeiro passo para retorná-la ao cliente. Para tanto, utiliza-se a operação arrayCopyNonAtomic da máquina Util. Ela recebe o valor de jc_rnd, o buffer apdu (bf ), os índices a partir dos quais a cópia dos valores é efetivada (cast_short (0) e bf_offset) e quantos bytes serão copiados (le_val). Observe que o arquivo só será copiado no buffer caso diversas restrições sejam atendidas, tais como o fato do buffer não ser vazio (bf 6= []) e a quantidade de bytes inseridos a partir de bf_offset não ultrapassar o tamanho do buffer (sum_short(bf _offset, le_val) ≤ size(bf )). A abordagem de antecipar os erros que possam surgir pela aplicação de operações com valores incorretos é utilizada durante toda implementação para assegurar a robustez do desenvolvimento. Caso a geração tenha sido bem-sucedida, o estado global do passaporte é atualizado para CHALLENGED (vol_state.set(CHALLENGED)) e o valor do número randômico é retornado em data_out (data_out := jc_rnd). Destaca-se que o código de envio de data_out ao cliente, comum a todas as operações que retornam informações, encontra-se inserido no método process da implementação Applet. processSelectFile (apdu_p, data_in) = VAR v_st, per_st,bf, lc_data, fid, file IN v_st ← vol_state.getFirst; per_st ← per_state.get_num; bf ← apdu.getBuffer; vol_state.set(NO_FILE_SELECTED); IF per_st = LOCKED THEN ISOException.throwIt(ex_processSelectFile0) ELSIF ¬ (v_st = MUTUAL_AUTHENTICATED) THEN ISOException.throwIt(ex_processSelectFile1) ELSE fid := data_in(1); lc_data := cast_byte(8); IF (fid ≥ EF_DG1_INDEX ∧ fid ≤ SOS_LOG_INDEX) THEN file ← fileSystem.getFile(fid); IF (file 6= []) THEN sel_file.set_num(fid); vol_state.set(FILE_SELECTED) ELSE ISOException.throwIt(SW_FILE_NOT_FOUND) END ELSE ISOException.throwIt(SW_INTERNAL_ERROR) END END END; Figura 7.14: Passport_JC_imp - operação concreta processSelectFile. Ressalta-se que a operação processSelectFile (figura 7.14) não retorna nenhuma informação por meio do buffer. Sua funcionalidade principal é atualizar o arquivo selecionado (a variável 151 de estado selectedFile) com o valor fornecido pela aplicação host. Em seu modelo concreto, após as verificações de estado, o identificador do arquivo é obtido do primeiro byte do parâmetro data_in (fid := data_in(1)). Na sequência do código de processSelectFile é feita a leitura do arquivo, caso o identificador esteja na faixa correta (file ← fileSystem.getFile(fid), e a atualização das informações do estado relativa à seleção do arquivo (sel_ f ile.set_num(fid) e vol_state.set(FILE_SELECTED)). Na seção 7.7.3 a seguir descreve-se o componente da implementação que refina o modelo da classe Applet da API Java Card e realiza o processamento das chamadas aos serviços do desenvolvimento Passport_JC. 7.7.3 Desenvolvimento do Applet Java Card É importante enfatizar que o applet Java Card é sintetizado a partir do desenvolvimento da máquina Applet da API Java Card descrita nesta seção. Nessa abordagem, o desenvolvimento Passport_JC é incluso (importado, na implementação). Assim, em suas componentes de mudança de estado, o módulo Applet, bem como os seus refinamentos, pode utilizar os serviços de Passport_JC sem interferir em seu estado interno. Além disso, promove-se uma melhor modularização do desenvolvimento, com o módulo applet gerenciando as operações da classe applet e a especificação Passport_JC implementando o serviço global da aplicação. O componente Applet é essencial à verificação de conformidade entre os modelos abstratos e concretos da aplicação. No refinamento de Passport, as operações abstratas são relacionadas às suas correspondentes concretas através do módulo Applet, conforme detalhado na seção 7.8. A figura 7.15 exibe as máquinas referenciadas (sees e includes) e a definição do estado do applet. MACHINE Applet EXTENDS Object SEES JByte, JShort, Shareable, Apdu_Poperties, (...), InterfaceContext, FileSystem_Context INCLUDES app.Passport_JC CONCRETE_VARIABLES perState, volState, selectedFile, rnd, ssc INVARIANT perState ∈ JBYTE ∧ volState ∈ seq1(JBYTE) ∧ selectedFile ∈ EF_DG1_INDEX .. SOS_LOG_INDEX ∧ rnd ∈ seq1 (JBYTE) ∧ ssc ∈ seq1 (JBYTE) ∧ perState = app.jc_perState ∧ volState = app.jc_volState ∧ selectedFile = app.jc_selectedFile ∧ rnd = app.jc_rnd ∧ ssc = app.jc_ssc INITIALISATION perState := app.jc_perState || volState := app.jc_volState || selectedFile := app.jc_selectedFile || rnd := app.jc_rnd || ssc := app.jc_ssc Figura 7.15: Módulos referenciados e estado da máquina Applet. 152 É importante enfatizar a inclusão do módulo Passport_JC, permitindo o acesso às suas operações na operação processAPDU de Applet, que atua como interface para o modelo concreto. Observa-se que, no invariante, o estado do módulo abstrato foi acrescentado e relacionado ao estado da instância app.Passport_JC, possibilitando a verificação global do desenvolvimento como um refinamento de Passport (Passport_ref, seção 7.8). A reinserção do estado em Applet teve por objetivo contornar uma limitação da linguagem B, que não permite o acesso a uma instância de uma máquina inclusa através de outra instância, como no caso em questão, através da notação applet.app.Passport_JC. Outro ponto a ser enfatizado na máquina Applet diz respeito à divisão de responsabilidades entre os métodos process e processAPDU (fig. 7.16). O primeiro é um método abstrato da classe Applet, devendo portanto manter a sua interface inalterada e, em Java, ser implementado na subclasse. O método process é a porta de entrada para o applet, sendo essa característica explorada no tratamento da recepção do buffer apdu e no retorno de dados ao cliente, conforme detalhado posteriormente ao analisarmos a sua implementação B. Por outro lado, a operação processAPDU foi acrescentada para realizar a interface entre o modelo abstrato dos serviços e o seu correspondente concreto. Ela recebe em seus parâmetros os diversos campos do buffer apdu, fornecidos pelo cliente, e retorna a sequência de bytes data_out com os dados retornados pelas operações. process(apdu) = PRE apdu ∈ APDU THEN skip END; data_out ← processAPDU (apdu_p, cla, ins, p1, p2, lc, data_in, le) = PRE apdu_p ∈ APDU ∧ cla ∈ JBYTE ∧ ins ∈ JBYTE ∧ p1 ∈ JBYTE ∧ p2 ∈ JBYTE ∧ lc ∈ JBYTE ∧ le ∈ JBYTE ∧ data_in ∈ seq(JBYTE) ∧ data_in 6= [] ∧ size (data_in) ≥ 1 ∧ size (data_in) ≤ 127 THEN SELECT ins = processGetChallenge_INS ∧ app.jc_perState = HAS_MUTUAL_AUTHENTICATION_KEYS ∧ ¬ (app.jc_volState(1) = MUTUAL_AUTHENTICATED) THEN data_out ← app.processGetChallenge(apdu_p, boolean_of_byte(p1), data_in ) (. . . ) ELSE data_out :∈ seq(JBYTE) END || perState := app.jc_perState || volState := app.jc_volState || selectedFile := app.jc_selectedFile || rnd := app.jc_rnd || ssc := app.jc_ssc END; Figura 7.16: Operações abstratas process e processAPDU (parte). No corpo da operação abstrata processAPDU, os serviços do passaporte são requisitados a partir do seu código de operação (ins), obedecendo as restrições da pré-condição. Os parâmetros de entrada são obtidos da interface de processAPDU. No estudo de caso, foram utilizados os campos data_in, que corresponde ao array data do buffer e p1, valor de 1 byte relacionado ao parâmetro protectedAPDU recebido pelos serviços. O código da figura 7.16 exibe a chamada 153 da operação processGetChallenge, recebendo o conjunto que representa o objeto apdu, o byte em p1 convertido para boolean através da função boolean_of_byte da máquina InterfaceContext (em detalhes na seção 7.8) e a sequência data_in. Com o propósito de estabelecer que o applet a ser gerado advém de fato do modelo Applet, deve-se proceder com o seu refinamento e, em última etapa, a sua implementação B. Devido a semelhança do componente abstrato, omitiu-se desta seção o primeiro refinamento, que pode ser consultado em apêndice. No modelo concreto JC_Applet_imp (implementação completa no Apêndice C.7), deve-se fornecer a implementação definitiva das operações do applet. No presente estudo de caso, implementou-se em maior detalhe os métodos process e processAPDU. Observa-se que a implementação fornecida à operação process terá maior relevância apenas durante a execução da aplicação Java Card. Dividiu-se a apresentação de process em três partes, compreendendo as figuras 7.17 a 7.19. Inicialmente (fig. 7.17), foi inserido código para recuperar o buffer apdu (bf ← apdu.getBuffer) e obter cada componente do buffer, tais como o identificador de instrução (ins), os parâmetros (p1 e p2) e os dados do campo data, que são copiados no array de dados data_in (Util.arrayCopyNonAtomic (bf, OFFSET_CDATA, data_in, 0, lc)). process(apdu) = VAR bf, it cla, ins, p1, p2, lc, data_in, lr, data_out, apdu_state, res res2 IN bf ← apdu.getBuffer; (...) IF (size (bf) ≥ 6 ∧ (...) bf(OFFSET_LC) ≤ MINBYTE ∧ bf(OFFSET_LC) ≥ MAXBYTE ) THEN cla := bf(OFFSET_CLA); ins := bf(OFFSET_INS); p1 := bf(OFFSET_P1); p2 := bf(OFFSET_P2); lc := bf(OFFSET_LC) ELSE data_in := [0]; data_out := [0]; apdu_state ← apdu.getCurrentState; IF (lc > 0) THEN IF (apdu_state = STATE_INITIAL) THEN res ← apdu.setIncomingAndReceive; res2 ← Util.arrayCopyNonAtomic (bf, OFFSET_CDATA, data_in, 0, lc) END END; (...) Figura 7.17: Implementação do método process - obtenção de parâmetros e entrada de dados. Destaca-se que a segunda tarefa a ser executada no método process consiste na chamada ao método processAPDU (fig. 7.18). Para tanto, as informações obtidas do buffer devem ser copiadas na interface de processAPDU, que então as repassa ao serviço requisitado na aplicação (de acordo com o campo ins). No código de process, o acesso aprocessAPDU encontra-se 154 comentado devido ao método B não permitir que uma operação invoque outra operação da mesma máquina (no caso JC_Applet_imp). Assim, após a geração de código, o usuário deve retirar o comentário desse trecho de código, que já encontra-se na notação Java. process(apdu) = (...) lr := cast_byte(0); /∗ ===> Uncomment here to call service operations <=== data_out = processAPDU ( apdu, cla, ins, p1, p2, lc, data_in, le ); lr = data_out.length ; */ (...) Figura 7.18: Implementação do método process - chamada de processAPDU A última etapa no código de process se refere ao envio de dados no buffer apdu (fig. 7.19), o que irá ocorrer caso o número de bytes retornados em data_out seja maior que zero. Ressalta-se que esse trecho de código é semelhante para qualquer operação que deseje realizar o retorno de uma informação no buffer APDU. No caso, deve-se invocar, nessa ordem, as operações, setOutgoing, setOutgoingLength e sendBytes, da mesma forma como foi demonstrado no capítulo 2. No código da implementação em B, assegura-se a correção na ordem em que as operações devem ser chamadas através da verificação, na pré-condição de cada operação, do estado da máquina APDU. process(apdu) = (...) IF lr > 0 THEN apdu_state ← apdu.getCurrentState; IF (lr ≥ 0 ∧ lr ≤ 256 ∧ sum_short(0, lr) ≤ BUFFER_LENGTH) THEN IF (apdu_state = STATE_INITIAL ∧ apdu_state 6= STATE_OUTGOING) THEN res ← apdu.setOutgoing ELSIF (apdu_state = STATE_OUTGOING ∧ apdu_state 6= STATE_OUTGOING_LENGTH_KNOWN) THEN apdu.setOutgoingLength(lr); apdu.sendBytes (0, lr) ELSE ISOException.throwIt (SW_SECURITY_STATUS_NOT_SATISFIED) END ELSE ISOException.throwIt (SW_SECURITY_STATUS_NOT_SATISFIED) END END END END; Figura 7.19: Implementação do método process - saída de dados. A figura 7.20 apresenta a operação processAPDU do módulo JC_Applet_imp. De modo semelhante à operação abstrata, agora utilizando-se de substituições condicionais, no corpo da operação implementou-se a chamada a cada serviço do passaporte a partir do seu código de instrução correspondente (declarados em Passport_JC_FF_Context, figura 7.9). Observase que a pré-condição dos serviços é sempre verificada antes da sua aplicação. O parâmetro protectedAPDU, por se tratar de apenas 1 byte (um valor booleano) foi obtido do parâmetro p1. 155 Demais dados de entrada são obtidos de data_in e repassados ao parâmetro data_in do serviço. Nos casos em que há saída de dados, essa é direciona à sequência de bytes de retorno data_out. data_out ← processAPDU ( apdu_p, cla, ins, p1, p2, lc, data_in, le ) = VAR per_st, vol_st, protectedAPDU IN per_st ← app.getPersistentState; vol_st ← app.getVolatileState; protectedAPDU := FALSE; IF ins = processGetChallenge_INS THEN IF per_st = HAS_MUTUAL_AUTHENTICATION_KEYS ∧ ¬ (vol_st = MUTUAL_AUTHENTICATED) THEN IF p1 = cast_byte (1) THEN protectedAPDU := TRUE END; data_out ← app.processGetChallenge (apdu_p, protectedAPDU, data_in ) END ELSIF ins = processMutualAuthenticate_INS THEN IF (vol_st = CHALLENGED ∧ ¬ (vol_st = MUTUAL_AUTHENTICATED)) THEN IF p1 = cast_byte (1) THEN protectedAPDU := TRUE END; data_out ← app.processMutualAuthenticate (apdu_p, protectedAPDU) END ELSIF (...) ELSE data_out := [] END END; Figura 7.20: Implementação do método processAPDU 7.8 Verificação do Refinamento entre Especificação Abstrata e o Modelo Java Card A verificação da relação de refinamento entre o modelo abstrato Passport e o seu correspondente concreto Passport_JC é feita em conformidade com a abordagem de adaptação descrita no capítulo 4 (seção 4.4). A ideia básica consiste em demonstrar que, mesmo possuindo interface distinta da máquina abstrata Passport, o modelo concreto equivale a um refinamento dessa. As obrigações de prova geradas para este refinamento foram corretamente verificadas com o auxílio do provador da ferramenta Atelier B. O refinamento Passport_ref da máquina Passport, referencia o módulo Applet, que por sua vez contém a instância da máquina Passport_JC concreta. O invariante de Passport_ref estabelece a relação de refinamento entre o estado abstrato e o correspondente concreto através da instância na máquina Applet (perState = applet.perState ∧ ...). Com relação às operações, a abordagem de refinamento consiste em conciliar a incompatibilidade entre as interfaces, ao traduzir os parâmetros abstratos nos correspondentes concretos, 156 REFINEMENT Passport_ref REFINES Passport SEES JByte, JShort, JBoolean, ISO7816,Apdu_Properties, Passport_Context, Passport_JC_FF_Context, FileSystem_Context, InterfaceContext INCLUDES fileSystem.FileSystem, applet.Applet INVARIANT perState = applet.perState ∧ volState = applet.volState ∧ selectedFile = applet.selectedFile ∧ rnd = applet.rnd ∧ ssc = applet.ssc INITIALISATION skip Figura 7.21: Refinamento Passport_ref, máquinas inclusas e invariante. tais como os campos p1, p2 e o array data_in, fornecidos à operação processAPDU. A partir do código de instrução (campo ins), a operação processAPDU irá requisitar o serviço concreto correspondente. O serviço é então realizado sob os dados fornecidos, alterando as variáveis de estado e o estado global da aplicação, de modo equivalente ao realizado na operação abstrata. Após a execução do serviço, os dados recebidos em data_out são retornados e convertidos para o tipo correspondente na saída abstrata. Observa-se que a compatibilidade de tipos na conversão/decodificação dos dados é verificada por meio de funções inseridas na máquina InterfaceContext (fig. 7.22), vista pelo componente abstrato, pelo modelo Applet e pelo componente concreto, quando necessário. Cada função é definida em par, uma que converte e a outra que realiza a operação contrária. Por exemplo, a função seq_of_byte recebe um byte e indica a inserção desse byte em uma sequência. Por sua vez, byte_of_seq, realiza a operação contrária, indicando a conversão de um byte a partir de uma uma posição na sequência. MACHINE InterfaceContext SEES JByte, JShort, JBoolean, FileSystem_Context CONCRETE_CONSTANTS seq_of_byte, byte_of_seq, seq_of_short, short_of_seq, boolean_of_byte, byte_of_boolean PROPERTIES seq_of_byte ∈ JBYTE → 7 seq1(JBYTE) ∧ seq_of_byte = λ (v1).(v1 ∈ JBYTE | [v1]) ∧ byte_of_seq ∈ seq1(JBYTE) → 7 JBYTE ∧ byte_of_seq = λ (v1).(v1 ∈ seq1(JBYTE) ∧ size(v1) ≥ 1 ∧ v1(1) ∈ JBYTE | v1(1)) ∧ seq_of_short ∈ JSHORT → 7 seq1(JBYTE) short_of_seq ∈ seq1(JBYTE) → 7 JSHORT short_of_seq −1 = seq_of_short ∧ ∧ ∧ boolean_of_byte ∈ JBYTE → 7 JBOOLEAN boolean_of_byte = {1 7→ TRUE} boolean_of_byte = {0 7→ FALSE} ∧ ∧ ∧ byte_of_boolean ∈ JBOOLEAN → 7 JBYTE byte_of_boolean = {TRUE 7→ 1} byte_of_boolean = {FALSE 7→ 0} ∧ ∧ END Figura 7.22: Máquina Interface Context. 157 Como exemplo, na figura 7.23, tem-se o refinamento da operação abstrata processGetChallenge. A conversão do parâmetro protectedAPDU em um byte, sendo esse enviado no campo p1, é feita com o auxílio da função byte_of_boolean (p1 := byte_of_boolean (protectedAPDU)). Por sua vez, o trecho de código data := seq_of_short(le) representa a conversão de le, um valor short, na sequência data. O retorno de processAPDU corresponde ao número randômico gerado, que por ser uma sequência de bytes, não necessita de conversão ao ser recebido pela variável de saída rnd_val. rnd_val ← processGetChallenge (protectedAPDU, le) = VAR apdu, cla, ins, p1, p2, lc, data, data_out IN apdu :∈ APDU; cla := CLA_NUMBER; ins := processGetChallenge_INS; p1 := byte_of_boolean (protectedAPDU); p2 := cast_byte (0); lc := cast_byte (2); data := seq_of_short(le); SELECT size (data) ≥ 1 ∧ size (data) ≤ 127 THEN data_out ← applet.processAPDU(apdu, cla, ins, p1, p2, lc, data, cast_byte(8)); rnd_val := data_out END END; Figura 7.23: Operação refinada processGetChallenge. 7.9 Considerações Finais Inicialmente, é importante enfatizar que o desenvolvimento do estudo de caso do passaporte eletrônico possibilitou a validação e o aprimoramento do método BSmart, dos módulos do KitSmart utilizados, bem como de todo o suporte ferramental ao método. Apesar de, na versão atual, não se tratar de um desenvolvimento de grande complexidade ou de nível industrial, ele exercita boa parte da notação B de cláusulas, substituições e expressões. Em relação ao método, o estudo de caso motivou a decisão de separar o módulo applet do módulo desenvolvimento principal dos serviços Java Card. Caso não fosse feito dessa forma, teríamos os métodos do applet inseridos diretamente no desenvolvimento, o que criaria uma distorção em relação à máquina abstrata, uma vez que a implementação concreta teria mais serviços que o modelo abstrato, o que não é permitido para o refinamento no B clássico. Além disso, os serviços do próprio módulo Passport_JC não poderiam ser requisitados dentro dos métodos do applet (process e install, por exemplo) caso eles fizessem parte de Passport_JC . Ressalta-se que os módulos do KitSmart foram constantemente revisados e aprimorados 158 durante o desenvolvimento do estudo de caso. Modificou-se os tipos de dados, por exemplo, de array (função total) para sequência de byte, tendo em vista facilitar as obrigações de prova. Assim como pré-condições e implementação dos modelos de tipos de dados básicos e das classes da API utilizadas foram corrigidos. Observa-se, ainda, que nem todas as classes foram exercitadas, o que deve ser feito como trabalho futuro através de outros estudos de caso. A utilização da especificação Passport na validação da tradução também foi de suma importância, uma vez que no módulo de tradução, muito da notação B, dos tipos de dados e da composição de máquinas pôde ser verificado. Algumas diretrizes de como proceder com a implementação e decisões de projeto também foram estabelecidas, e são detalhadas no capítulo 5 que descreve a tradução do refinamento concreto B para Java Card. O desenvolvimento foi traduzido em classes Java Card e realizou-se a verificação sintática para o Java 1.3 através da IDE Eclipse. A efetiva instalação e teste da aplicação em um cartão (ou simulador) ainda não foi realizada, sendo objeto de trabalho futuro. É importante destacar o esforço de prova no desenvolvimento do passaporte seguindo o método BSmart. No total, foram geradas 1155 obrigações de prova, em maior número nas implementações B, sendo 255 de Passport_JC_imp, 160 de Applet_i e 137 do refinamento Passport_ref. No estudo de caso, é possível realizar a maior parte da verificação com o provador automático da ferramenta Atelier B. Por outro lado, ao utilizar o provador interativo, percebeuse um ganho significativo no tempo de prova, ao se aplicar os comandos adequados de forma manual. Sendo assim, preferiu-se utilizar esse último modo de prova durante a maior parte do desenvolvimento. Além disso, com o uso do modo interativo, verificou-se que certos padrões de prova se repetiam. Nesse caso, uma melhoria a ser feita é a criação de táticas de prova que auxiliem o provador a aplicar diretamente os passos de verificação para esses casos. Como trabalhos futuros pretende-se, na especificação cartão, realizar o desenvolvimento dos demais serviços que são necessários aos protocolos AA e EAC. Em relação ao cliente, espera-se identificar um subconjunto dessa parte da aplicação que poderia ser inserido, motivando a geração do cliente a partir da sua implementação B, e não apenas com base na interface das operações do modelo abstrato, como é feito atualmente. 159 8 Trabalhos Relacionados O presente trabalho teve início como parte do projeto Engineering of Smart Card Applications (SMART), que tem por objetivo o estudo e o desenvolvimento de técnicas e ferramentas para a aplicação de engenharia de software rigorosa, baseada em métodos formais, ao desenvolvimento de aplicações para cartões inteligentes. Outros frutos desse projeto são apresentados na dissertação de mestrado de Plácido Neto [Net07], bem como nas monografias de graduação de Kátia Moraes [Mor07] e Thiago Dutra [Dut06]. Plácido Neto [Net07] realizou um estudo quanto à aplicabilidade de JML para a verificação em tempo de execução de aplicações Java Card. O esforço inicial deste estudo consistiu na definição de um subconjunto da notação JML contendo apenas construções compatíveis com Java Card. Posteriormente, foi construída uma primeira versão de um compilador para esta nova linguagem, denominada de Java Card Modelling Language (JCML). Utilizando-se o compilador, é possível a geração de anotações em JCML no código de uma aplicação Java Card para a verificação de pré-condição e de invariante. O trabalho de Kátia Moraes [Mor07] desenvolveu o protótipo inicial para a geração da API para a aplicação host. Nele definiu-se um conjunto de classes que efetuam de forma transparente para o usuário a comunicação com alguma API (Smart Card I/O, p. ex.) e que fazem a conversão de tipos e montagem de comandos APDU. Na tese, essas classes foram remodeladas, tendo em vista uma melhor distribuição de responsabilidades e promover o reuso de parte dos componentes. Além disso, toda a implementação, antes desenvolvida em Java, foi refeita em C++ utilizando a API B-Compiler [Cle12b]. A biblioteca KitSmart foi inicialmente projetada e implementada no trabalho de conclusão de curso de Dutra [Dut06]. Foram desenvolvidos modelos para manipulação de tipos inteiros, de datas e hora e valores de medida. Observa-se que Moraes, juntamente com bolsistas de iniciação científica e com o autor desta tese, contribuíram para a continuidade do desenvolvimento do KitSmart, através do aprimoramento dos módulos tratados no trabalho de Dutra e das versões iniciais dos modelos da API Java Card. Posteriormente, esse ramo do KitSmart foi especificado 160 em sua totalidade no trabalho de Simone Santos [San12], que também contribuiu com um guia para uso e criação de novos componentes no Kit. O trabalho de Santos é descrito em maiores detalhes no capítulo 6. O presente capítulo relata a pesquisa e as ferramentas de desenvolvimento que se encontram no escopo desta tese. Por serem áreas com maior relacionamento ao trabalho proposto, forneceu-se maior evidência aos trabalhos que propõem aplicar técnicas formais ao desenvolvimento smart card, a geração de código a partir da linguagem B e as ferramentas de apoio ao desenvolvimento formal. Como forma de melhor organizar a discussão, dividiu-se o capítulo em seções relacionadas a cada um dos tópicos mencionados. 8.1 Ferramentas de Suporte ao Desenvolvimento Formal de Software em B e Event-B A presente seção descreve algumas ferramentas de suporte ao desenvolvimento formal de software. Ela foi propositalmente inserida antes das demais devido às ferramentas ora apresentadas serem utilizadas por diversos trabalhos que são descritos nas próximas seções. Será detalhado de forma geral o propósito de cada ferramenta e as suas principais funcionalidades. Nas demais seções, os aspectos específicos de cada uma delas podem ser revisitados, como, por exemplo, a sua utilização para a geração de código. A ferramenta JBtools [Voi02] foi uma das primeiras abordagens acadêmicas a prover um gerador de código da linguagem B para linguagens orientadas a objetos. No caso, as linguagensalvo escolhidas para a tradução foram Java e C# e, até certo ponto, conforme discutir-se-á posteriormente, Java Card. Além de geração de código, o ambiente de desenvolvimento JBtools fornece verificação de tipos em uma especificação B, geração de documentação em HTML e uma biblioteca para a manipulação de um módulo B, denominada de B Object Library BOL. É interessante detalhar o componente BOL, devido à sua utilização na versão inicial da ferramenta BSmart. A BOL contém uma classe para cada elemento da especificação B. Após a verificação de tipos bem-sucedida em uma especificação, é gerado um arquivo XML contendo a representação de cada elemento de notação do módulo que foi verificado como uma marcação XML. Essas marcações são lidas e proporcionam a montagem de uma árvore de objetos da BOL. A partir desses objetos é então possível a fácil manipulação de um módulo B para diversas tarefas, tais como análise do código, geração de um novo módulo B ou refinamento e tradução. Ressalta-se também a plataforma de desenvolvimento Brillant [MC10], uma iniciativa de código aberto para suporte e experimentação com o método formal B. O conjunto de software 161 definido pelo Brillant inclui parser, verificador de tipos, tradutor de código e um gerador de obrigações de prova. O gerador ainda não está completo, mas implementa a maior parte das construções e propriedades de prova descritas no B-Book [Abr96]. As obrigações de prova vêm sendo geradas em um formato compatível com o provador de teoremas Coq [INR10]. De forma similar ao que é feito para o JBtools, para permitir a integração das diversas ferramentas, um formato intermediário em XML do desenvolvimento é gerado. O Atelier B [Cle12a] é uma importante ferramenta de suporte ao método B, sendo utilizada em diversos projetos na indústria e na academia. O Atelier B fornece um conjunto de ferramentas integradas a uma IDE, dentre as quais destaca-se um verificador de tipos, um gerador de obrigações de prova e um provador que fornece a verificação das obrigações de prova de forma automatizada ou com assistência do usuário (interativa), essa última deve ser utilizada quando determinada obrigação de prova não consegue ser verificada de forma automática. Cumpre observar que além das ferramentas mencionadas, o Atelier B, na sua versão mais recente (4.1), inclui a ferramenta Bart e um gerador de código de B para C, denominado de ComenC [Cle12c]. O software Bart possibilita a geração automatizada de refinamentos em uma especificação ou refinamento B até a sua implementação. Inicialmente, deve-se especificar o padrão de refinamento desejado para as variáveis, a inicialização e as substituições das operações do módulo alvo do refinamento. Essa especificação é feita por meio de regras de refinamento e submetida ao Bart, que tenta casar o padrão estabelecido nas regras com as construções do módulo a ser refinado. Sempre que um padrão é estabelecido, a regra de refinamento associada é aplicada, resultando, ao final do processo, na implementação concreta do módulo refinado. Observa-se que os refinamentos obtidos devem ser verificados, assim como qualquer outro refinamento, usando as obrigações de prova padrão de B. No que concerne a animação de uma especificação B, que pode ser entendido como o processo de simulação da “execução” da especificação para fins de teste, destacam-se as ferramentas ProB [LBBP10] e Brama [Ser07]. A ferramenta ProB permite, além da animação, a verificação de modelos em uma especificação B ou Z. Em suas primeiras versões, ela utilizava a biblioteca BOL provida pelo JBtools. Nas versões mais recentes do ProB, essa biblioteca foi substituída por um parser e verificador de tipos B desenvolvidos pelos seus próprios autores. Por sua vez, o software Brama é desenvolvido pela Clearsy, a empresa que fornece a ferramenta Atelier B. Brama permite a construção de um modelo visual em Flash que conecta-se ao módulo de animação da especificação, permitindo assim a execução de uma especificação de maneira visualmente mais atrativa. 162 O projeto Deploy (anteriormente Rodin) 1 tem por objetivo a criação de técnicas e ferramentas para o desenvolvimento de sistemas complexos de software, integrando métodos formais a técnicas de desenvolvimento de sistemas tolerantes a falhas. O projeto vem sendo desenvolvido por grandes centros de ensino e pesquisa europeus, como Instituto Federal de Tecnologia (ETH) de Zurique e a Universidade de Southampton, em parceria com empresas privadas, como a Bosch, Siemens e a Space Systems Finland, que fornecem estudos de caso de sistemas de grande porte e testam as técnicas e ferramentas propostas no projeto. Atualmente, a ferramenta Rodin, que vem sendo desenvolvida no âmbito deste projeto, é a implementação de referência para o formalismo Event-B [Abr10]. A ferramenta Rodin é composta por um kernel básico, distribuído como um plugin para a plataforma Eclipse, e que serve de base para a integração de diversos plugins com ferramentas específicas para suporte à metodologia, dentre os quais, destacam-se o plugin para o desenvolvimento básico com Event-B e outro que inclui o gerador de obrigações de prova e o provador da ferramenta Atelier B. Ferramentas externas também podem ser integradas ao ambiente, tais quais o ProB, Brama e o software UML-B, que permite a tradução de diagramas em uma notação similar à UML para especificações na linguagem B e Event-B. O Rodin é uma plataforma aberta e extensível que vem sendo continuamente aprimorada e atualizada com novos plugins. A versão estável atual do Rodin pode ser obtida no sítio http://www.event-b.org/install.html. Ressalte-se que a ferramenta BSmart, desenvolvida no presente trabalho, não se propõe, a princípio, a ser tão abrangente quanto o plugin Rodin ou, ainda, a ferramenta Atelier B. O conjunto de software oferecido pelo BSmart visa apoiar o desenvolvimento formal de aplicações smart cards, seguindo o método formal B. Para tanto, o básico de serviços de suporte ao desenvolvimento em B é oferecido, como a verificação de tipos e de obrigações de prova através da integração com as ferramentas do Atelier B e Batcave [MdMJD+ 08]. Na versão inicial, o processo com o Atelier B em linha de comando era iniciado e posteriormente acessado dentro do ambiente do BSmart, na plataforma Eclipse. Atualmente, a integração tornou-se ainda mais forte, com as ferramentas do BSmart podendo ser incorporadas ao ambiente gráfico do Atelier B através do seu mecanismo de extensão. Além disso, as ferramentas utilizam o software BCompiler [Cle12b], o mesmo utilizado pelo Atelier B e seus programas, para possibilitar a manipulação de um módulo B e a geração de código. Observa-se que, com o uso do BCompiler, conseguiu-se uma solução estável para a verificação de tipos e a manipulação da especificação, com a vantagem de alinhar o desenvolvimento com a linguagem B do Atelier B. Conforme delineado na seção 4.6, as ferramentas fornecidas 1 site do projeto Deploy: http://www.deploy-project.eu/ 163 pelo BSmart compreendem um gerador de refinamento full function, um gerador para a aplicação host a partir da especificação inicial e o tradutor da implementação B para Java e Java Card. Essa última também verifica se a implementação satisfaz as restrições observadas para uma classe Java Card. 8.2 Verificação de Aplicações Smart Cards O estudo da verificação de hardware e software é um tópico de extrema relevância de pesquisa para smart cards. No caso particular de Java Card, seu ambiente de execução simplificado e a linguagem com notação simples, tornam o esforço de verificação menos complexo quando comparado com uma linguagem como o Java padrão. Java Card implementa apenas os tipos primitivos byte, short e int, possui uma API restrita, e não apresenta recursos como threads e coleta de lixo pela máquina virtual. Sendo assim, a verificação pode concentrar-se em menos aspectos da linguagem e do ambiente de execução. A Common Criteria é uma organização que tem por objetivo avaliar a segurança de produtos de tecnologia, de acordo com diversos níveis de segurança (EALs). Um produto é classificado em um certo nível (de 1 a 7) a partir da análise das técnicas de especificação e desenvolvimento que foram empregadas na sua construção e da avaliação de que o produto cumpre com os requisitos de segurança especificados. No nível mais alto de certificação, exigem-se a utilização de métodos formais e o seu relacionamento com a especificação de políticas de segurança. Nesse sentido, o trabalho de Motré e Terí [MT00] estabelece modelos de políticas de segurança para o componente Java Card Runtime Environment (JCRE) da máquina virtual Java Card visando atingir os níveis 4, 5 e 7 de certificação no Common Criteria. O nível 6 não foi incluso por ser totalmente coberto pelo nível 7, o primeiro no qual se exige a aplicação de técnicas de verificação formal. Como estudo de caso, especificou-se a execução de instruções de bytecode geradas para um applet. O acesso a uma instrução de bytecode pelo interpretador Java pode requerer a autorização do mecanismo de firewall para verificar, por exemplo, se o applet possui as permissões necessárias para executá-la. Para o nível 4, o modelo de segurança foi implementado utilizando-se um diagrama de estados UML. Visando atingir o nível 5, a política de segurança anteriormente especificada foi formalizada como uma máquina B. Nesse caso, os estados do diagrama UML foram associados às variáveis do modelo em B. Através do invariante da máquina, foram descritas as possíveis transições entre os estados do modelo. Por fim, para atingir o nível 7, é necessário atestar que os componentes funcionais do sistema relacionados à política de segurança realmente a implementam. Dessa forma, as variáveis das funcionalida- 164 des sujeitas à verificação de segurança foram associadas às variáveis do modelo da política de segurança, que também foi especificado em B. Além disso, foi verificado que cada função de segurança implementava transições de estados que eram possíveis no modelo de segurança. Na mesma linha do trabalho anterior, Dadeau et. al. [DLMP08, DPT08] definem políticas de segurança para controle de acesso em aplicações para smart cards. Inicialmente, implementou-se, na linguagem B, o modelo da política de acesso a um objeto da aplicação Java Card por um agente externo utilizando uma operação do modelo. A política de acesso foi então mapeada para a implementação da operação em Java Card. A conformidade da operação implementada com o modelo de segurança é verificada com referência a todo trace de execução da implementação poder também ser executado pelo modelo de segurança. A base teórica da abordagem é feita utilizando o método B, dessa forma, foi possível também verificar a correção dos mapeamentos entre o modelo de segurança e a sua implementação através de obrigações de prova. Salienta-se que a especificação da API Java Card é uma área de aplicação de métodos formais a Java Card que vem recebendo bastante destaque. Uma vez que o código fonte da API normalmente não é fornecido, de uma forma geral, o objetivo desses trabalhos é permitir que verificações sejam feitas nas classes Java Card que a utilizam, por meio da especificação formal da API. Em Meijer [MP01] e Poll [PvdBJ01] pode-se vislumbrar a especificação, por meio de anotações em Java Modelling Language (JML) [Gar06], das classes JCSystem e APDU, respectivamente. O trabalho de Larsson [LM04] fornece como estudo de caso a especificação da classe para autenticação OwnerPin em JML e Object Constraint Language (OCL), fazendo uma comparação entre esses dois formalismos. Em sua dissertação de mestrado, Larsson [Lar03] especifica quase a totalidade da API Java Card 2.2.1 utilizando o formalismo OCL. Os trabalhos citados, bem como a especificação da API Java Card, foram utilizados como referência para a criação dos modelos da API Java Card da biblioteca KitSmart. Uma especificação em B das propriedades funcionais e de segurança do mecanismo de transação para Java Card foi feita em [SL99]. Essa especificação foi submetida a 11 refinamentos, sendo o último nível a implementação em B. Em todo o desenvolvimento foram geradas e corretamente verificadas 1500 obrigações de provas. A partir da implementação B foi feita a geração “manual” de código para a linguagem C. A verificação do bytecode Java Card geralmente é efetuada fora do cartão para assegurar que a aplicação a ser instalada segue a especificação Java Card e não compromete a segurança da máquina virtual ou de outros applets instalados no cartão [Ort01]. Em [CBR02] foi desenvolvido um verificador de bytecode gerado em linguagem C a partir da sua especificação em 165 B, com otimizações de código e de utilização de memória, visando a sua instalação e execução dentro do cartão. Foram desenvolvidos modelos para a verificação estrutural e de tipos. A primeira forma de verificação visa garantir que o arquivo contendo a aplicação Java Card a ser inserido no cartão, denominado de Converted Applet (CAP), é válido, o que compreende a verificação de que as classes referenciadas realmente existem, que métodos final não sejam sobrescritos, a ausência de ciclos em hierarquias de herança, dentre outras. Já a verificação de tipos é utilizada para assegurar que a conversão de tipos em uma expressão seja feita somente entre tipos compatíveis. A interface entre esses modelos foi feita na especificação do arquivo CAP. O modelo do arquivo CAP é refinado pelo verificador estrutural, que especifica cada componente do arquivo CAP. A geração automática de testes baseada em métodos formais para aplicações Java Card é também uma linha de pesquisa que vem recebendo certa atenção, como pode ser visto por meio dos trabalhos de Martin e Bousquet [MdB01] e de Weelden [vWOF+ 05]. No trabalho de Martin e Bousquet [MdB01] inicialmente é feita a modelagem dos métodos do applet por meio de diagramas de estados UML. Estes são então traduzidos, com o auxílio da ferramenta UMLAUT [HPP00], em Labeled Transition Systems que servem como entrada para a ferramenta TGV [JJ05], que por sua vez gera os casos de teste. Os autores desenvolveram um pequeno programa para traduzir os casos de teste gerados em uma aplicação Java Card. Assim como no trabalho anterior, um estudo de caso de carteira eletrônica foi utilizado para a validação desta proposta. O programa gerado com base nos casos de teste inicializa as variáveis do applet e chama cada método, por meio de comandos APDU, passando diferentes parâmetros de teste. Por fim, as respostas enviadas pelos métodos são comparadas às saídas esperadas para cada resposta. Na validação da proposta de [MdB01], um processo manual de teste de caixa-preta feito diretamente na aplicação, ou seja, sem relação com o método proposto, foi feito e comparado com a proposta do artigo. De acordo com os autores, esta última mostrou-se superior em encontrar erros que aquela feita sem o auxílio das ferramentas. Em Weelden [vWOF+ 05] foi feito um estudo sobre a aplicabilidade de testes caixa preta baseado em especificações formais em aplicações Java Card e foram feitas comparações com outras abordagens de teste para Java Card. Para a geração dos dados, execução e análise dos testes foi utilizada a ferramenta GAST [KATP02], que recebeu como entrada uma especificação com as propriedades do sistema como funções na linguagem CLEAN 2 . Um estudo de caso de carteira eletrônica foi utilizado na aplicação de testes de forma incremental. Uma membro da equipe do projeto ficou responsável por desenvolver a especificação da aplicação e, uma 2 site da linguagem Clean: http://clean.cs.ru.nl/ 166 outra pessoa, a sua implementação. A cada novo ciclo de especificação/desenvolvimento, era aplicado o teste para verificar a conformidade da especificação com a implementação. Ao final do processo verificou-se que essa abordagem era adequada, uma vez que ela conseguiu detectar as inconsistências entre o modelo e a implementação final, tendo como resultado, segundo o autor, uma aplicação livre de erros. Alguns trabalhos que são parte do grupo de pesquisa do autor desta tese enveredam por esta área, tais quais os trabalhos de Fernanda Souza [Sou09] e de Ernesto Matos [dM12]. Até o presente momento, ainda não incorporou-se testes ao BSmart. No entanto, é interessante avaliar quais resultados desses trabalhos podem ser introduzidos no método. Na dissertação de Souza estudou-se a aplicação de técnicas para a geração de casos de testes caixa preta a partir de especificações formais na linguagem B. Apenas as variáveis relacionadas à pré-condição de uma operação B que modifica o estado da especificação são selecionadas para o teste. Foram adotadas as técnicas de análise do valor limite e particionamento de equivalência como critério para a seleção dos dados de testes, o que é feito com o auxílio da animação da especificação na ferramenta ProB [LBBP10]. Finalmente, a construção manual dos casos a serem testados é feita e submetida à ferramenta JUnit [JUn10]. Observa-se que o trabalho de Souza teve sequência na dissertação de mestrado de Ernesto Matos [dM12], na qual desenvolveu-se um estudo de caso para validar a proposta de Souza, o que resultou na descoberta de pontos que poderiam ser melhorados. No caso, foram aperfeiçoados os critérios de cobertura dos testes e acrescentou-se a extração de informações do corpo das operações durante a definição dos casos de testes (e não apenas das pré-condições). A dissertação de Matos também contribuiu com uma ferramenta para automatizar a geração dos casos de teste. O módulo de implementação B0, apesar de oferecer muitas construções de linguagem de programação imperativas, ainda não possui a mesma expressividade apresentada por essas linguagens quanto à facilidade de uso e recursos de programação disponíveis. Tendo em vista proporcionar maior poder de expressão à linguagem B, de modo à torná-la mais atrativa para a indústria, Burdy e Requet [BR03] propuseram extensões à notação B, definindo a teoria e obrigações de prova para os mecanismos de interrupção do fluxo de execução de instruções break, continue e return, semelhantes aos encontrados em linguagens como C e Java. Um mecanismo de especificação e lançamento de exceções em um módulo B também foi proposto. Nesse caso, as exceções podem ser utilizadas para se melhorar o entendimento da especificação, separando claramente os comportamentos “normais” de execução dos comportamentos de erro. Dessa forma, ao invés de retornar o comportamento de erro como um valor em um parâmetro de saída 167 de uma operação, especifica-se o comportamento de erro como uma exceção. No presente trabalho, também lida-se com comportamentos excepcionais em Java Card, que são modelados por meio de máquinas que especificam as classes de exceção da API Java Card. Esses modelos são importados no refinamento full function e possuem uma operação para indicar o lançamento de uma exceção. Ressalta-se que o modelo proposto é bastante simples, sendo uma exceção representada por um número de status, no formato da resposta APDU, que identifica o erro para a aplicação host cliente. 8.3 Geração de Código Há diversas ferramentas e trabalhos, tanto na academia quanto na indústria, relacionados à geração de código de B para linguagens imperativas ou orientadas a objetos e da linguagem de modelagem UML para B. Conforme mencionado no capítulo ??, quando o desenvolvimento em B encontra-se no nível de implementação, é possível utilizar na especificação apenas construções não-determinísticas e dados concretos. Além disso, a notação normalmente é restrita para um nível denominado B0, o que limita ainda mais os elementos de notação permitidos, tendo em vista facilitar o processo de geração de código e adequar a tradução a uma linguagem específica. O projeto B with Optimized Memory (BOM)3 estudou a aplicação de otimizações na tradução de código de B para as linguagens C e Java. Essas otimizações tem por objetivo a instalação da aplicação gerada em dispositivos com baixa capacidade de armazenamento e processamento, como os smart cards. A seguir são apresentados os trabalhos de Requet and Bossu [RB00], Bert [B+ 03] e Tatibouet [TRVH03], que estão no escopo do projeto BOM e cujos resultados, principalmente a proposta de Tatibouet, foram utilizados no presente trabalho quando da geração de código B para Java Card. É interessante ressaltar algumas otimizações propostas por Requet [RB00] com relação aos elementos da notação B que são gerados. No caso das constantes, a tradução é feita para diretivas de pré-processamento em C, de modo a evitar o consumo de memória por uma variável. Operações que são chamadas apenas uma vez tem a chamada substituída pelo seu código (processo de inlining), reduzindo assim o overhead na chamada de métodos. Máquinas que definem apenas constantes e conjuntos enumerados são traduzidas em arquivos de cabeçalho. Ainda no caso da tradução de máquinas, quando não há declaração de estado na implementação, não há necessidade de criação de ponteiros para uma instância específica da máquina. No caso das 3 site do projeto BOM: http://lifc.univ-fcomte.fr/˜tatibouet/WEBBOM 168 variáveis, o tradutor procede com a tradução para um tipo inteiro menor, como byte ou short, no momento em que elas são restritas para intervalos inteiros compatíveis com esses tipos. Com relação à geração de código para Java Card no presente trabalho, é possível proceder com otimizações semelhantes às do trabalho de Requet no código a ser gerado. Por exemplo, pode-se substituir a referência a um atributo constante (static final) por seu valor declarado na inicialização, ou substituir a chamada a um método final pelo seu código. No método BSmart, os tipos de dados de variáveis e expressões são fornecidos através da utilização das máquinas de biblioteca para os tipos primitivos de Java. Em cada biblioteca, tem-se a implementação de conjuntos que definem cada tipo e de operações que atuam sob o intervalo de valores permitidos para esse tipo. Dessa forma, o trabalho do tradutor consistirá apenas em gerar a implementação Java/Java Card correspondente ao tipo da variável, constante ou expressão em B. Bert et. al. [B+ 03] apresenta os resultados completos do trabalho anterior, já com uma versão do tradutor de B para C desenvolvida. Um estudo de caso de especificação e geração de código dos componentes Java Card Runtime Environment (JCRE) e intepretador de bytecode da máquina virtual Java Card foi desenvolvido para validar a ferramenta e o impacto das otimizações propostas. A especificação B foi composta de cerca de 10.000 linhas de código, complementada por 5.000 linhas de código em C para interface com dispositivos de baixo nível, gerenciamento de memória, etc. A geração foi feita para as plataformas de hardware smart cards Artmel AVR 8-bits e Smart MIPS 32-bits. Comparações com o tradutor da ferramenta Atelier B [Cle12a] atestaram que o tradutor otimizado de Bert permite redução de 78% no tamanho do código gerado para a plataforma AVR e de 58% no caso do código gerado para Smart MIPS. O tradutor original da ferramenta BSmart foi implementado como uma extensão do trabalho de Tatibouet [TRVH03, Voi02], que inclui as otimizações propostas no projeto BOM visando a inclusão de código Java em smart cards. Na proposta de Tatibouet, uma classe compatível com Java Card é gerada, mas a introdução dos aspectos específicos do applet deve ser feita de forma manual. Cumpre observar que, no trabalho de Tatibouet, é traduzida, a partir da implementação B, apenas uma classe Java Card contendo os serviços que o applet oferece. O componente applet deve ser criado manualmente pelo usuário e instanciar a classe Java gerada pela ferramenta. Portanto, o applet Java Card não é resultado direto da tradução a partir de B. Com relação à aplicação cliente, nenhum suporte de geração automatizado é fornecido pelo trabalho de Tatibouet. O BSmart agrega a vantagem da geração automatizada tanto do componente servidor (uma classe applet e uma classe de serviço), quanto da API de acesso os serviços pela aplicação host. 169 A tradução no trabalho de Tatibouet é feita por meio da ferramenta JBtools. O tradutor do BSmart inicialmente utilizou-se da API fornecida pelo JBtools para prover a geração da aplicação Java Card completa. A API para a aplicação cliente é gerada a partir da máquina B que inicia o desenvolvimento e o applet é o resultado final do processo de refinamento. Conforme mencionado na seção 4.6, devido a algumas dificuldades em se trabalhar com a API fornecida pelo JBtools para a verificação de tipos e a manipulação do código-fonte B, realizou-se no âmbito da tese, uma reengenharia das ferramentas do BSmart para utilizar-se da API fornecida pela ferramenta BCompiler. Neste caso, além de solucionar os problemas de instabilidade do JBtools, ao alinhar o desenvolvimento à notação da linguagem B fornecida pelo AtelierB, tornou-se possível beneficiar-se de todo o ambiente fornecido pela IDE do AtelierB através da integração dos componentes do BSmart. Com relação à tradução na ferramenta Atelier B, a sua versão atual inclui um tradutor de B para C. Ele recebe como entrada uma implementação B0 e os módulos abstratos dos quais a implementação depende. O tradutor produz como saída, para cada implementação, um arquivo de cabeçalho (.h) e um arquivo de código-fonte (.c). O cabeçalho contém as constantes e variáveis traduzidas, bem como a assinatura de cada função gerada a partir das operações do módulo B0. A implementação dessas funções é fornecida no arquivo de código-fonte, assim como uma operação contendo a inicialização das variáveis de estado da máquina. O processo de tradução renomeia cada elemento declarado (constantes, variáveis e métodos) prefixando-os com o nome do módulo B0. Esse processo é necessário para evitar conflito com elementos declarados em outros módulos incluídos na implementação (cláusula IMPORTS) e com palavras reservadas da linguagem C. É interessante ressaltar que o tradutor do Atelier B possui uma linguagem B0 bastante restritiva. Em virtude disso, não é possível a utilização do Atelier B para gerar código a partir das implementações desenvolvidas para o BSmart (ainda que para a linguagem C). Dessa forma, o tradutor do BSmart estabelece o seu próprio B0, permitindo a geração de código a partir de elementos da notação que não são considerados no Atelier B. A notação da linguagem B para geração de código não foi projetada tendo em vista a implementação dos conceitos de orientação a objetos, como herança, encapsulamento, classes, etc. No entanto, diversos estudos vêm sendo desenvolvidos no que concerne a técnicas e ferramentas para traduzir código B nessas linguagens e para a notação de diagramas em UML. Com relação à tradução para linguagens orientadas a objetos, além da proposta da ferramenta JBtools, uma iniciativa recente foi o desenvolvimento de um método para permitir a geração de código, inicialmente para Java, a partir de refinamentos em Event-B, descrita em 170 Edmunds e Butler [EB10]. Embora Event-B não seja a plataforma ideal para prover geração de código, e esse não seja de fato o seu propósito inicial, os autores desenvolveram uma linguagem intermediária, denominada de Object-Oriented Concurrent B (OCB), para a qual uma máquina Event-B é traduzida antes da geração de código. A OCB define uma linguagem concreta, mais próxima de Java, composta por processos (em uma sintaxe semelhante a uma classe Java) e dados que podem ser compartilhados entre processos. Os eventos são traduzidos para operações atômicas em cada processo, e ações nessas operações podem utilizar atribuição a dados locais e chamadas de procedimentos. É interessante observar que diversos estudos relacionam a tradução para diagramas em Unified Modeling Language (UML) a partir de B, como pode ser verificado por meio dos trabalhos de Idani [IL04], Fekih [FJM04] e Tatibouet [THV02]. Esses trabalhos fornecem idéias interessantes quanto à tradução de elementos da notação B para linguagens orientadas a objetos que podem ser diretamente aplicadas ao tradutor da ferramenta BSmart. O trabalho de [IL04] propõe um método composto por duas etapas. Na etapa preliminar, classes e associações são obtidas a partir dos conjuntos definidos no módulo B, sendo cada conjunto do módulo B um candidato potencial a uma classe, e uma associação definida pela análise do relacionamento entre dois conjuntos. A etapa seguinte determina se os conjuntos devem ser traduzidos para atributos ou para classes e o mapeamento de cada operação do módulo para as classes obtidas. Em [FJM04], a abordagem básica para a geração de classes determina que o domínio de uma relação torna-se uma classe se ele for um conjunto. Esta solução também é bastante simples, mas pode levar à geração de diagramas de classes incompletos. Na mesma linha dos trabalhos anteriormente citados, no trabalho de [THV02] cada conjunto e cada módulo B originam uma classe Java, em uma abordagem que também foi utilizada no trabalho deste mesmo autor para a tradução de B para Java [TRVH03]. Com relação à UML, essa solução não parece ser muito adequada, pois tende a gerar diagramas com muitas classes e associações. Ainda nesta linha de trabalhos, foi desenvolvida a ferramenta UML-B [SB06], que provê um ambiente para a criação de diagramas de modelagem, tendo por base parte da notação e da semântica da UML e os conceitos dos formalismos B e Event-B [SB08]. Inicialmente, os autores tentaram estender a UML utilizando perfis e estereótipos, no entanto, devido a uma maior personalização requerida pelo UML-B, criou-se uma semântica nova para diagramas específicos, visando a tradução do modelo para as linguagens B e Event-B. Em relação à tradução para Event-B, que é o foco atual do projeto, foram projetados quatro diagramas, a saber, pacote (package), contexto (context), classe (class) e máquina de estados (state machine). O diagrama de pacotes pode ser utilizado para se visualizar a relação entre máquinas e contextos. O diagrama 171 de classes representa as máquinas do sistema (contendo variáveis, eventos, invariantes, etc.) e associações entre máquinas. É possível definir variáveis na associação, que se tornam variáveis de uma das máquinas envolvidas. Por sua vez, o diagrama de contexto, assim como o contexto em Event-B, permite apenas a especificação de constantes e conjuntos e a definição das suas propriedades. Por fim, o diagrama de máquina de estados representa uma partição no comportamento da especificação, definida por meio de variáveis e eventos que especificam as partições individuais. 172 9 Considerações Finais Inicialmente, é interessante ressaltar que as aplicações inseridas em smart cards representam um mercado em vasta expansão. Setores de grande relevância econômica e social em todo o mundo, tais como telefonia móvel, transporte público, finanças, medicina e turismo, utilizam-se desta tecnologia como um meio seguro para identificação pessoal e armazenamento e recuperação de informações. Acrescente-se ao componente de segurança o fato de um smart card ser um dispositivo facilmente transportável, que não necessita de consulta a bases remotas para obtenção de dados e que requer energia apenas no momento de uma transação, quando está em contato com um leitor de cartão. As aplicações smart cards possuem certas características que as tornam candidatas ideais à adoção de algum formalismo em seu desenvolvimento. Elas armazenam e fornecem informações que não podem ser violadas e devem permanecer em estado consistente entre as várias ativações da aplicação, como valores monetários e dados pessoais. Além disso, o fato delas serem aplicações de pequeno porte, que especificam uma pequena API com os serviços oferecidos à aplicação cliente, facilita a sua especificação e verificação formal. O presente trabalho aborda o desenvolvimento em B para a linguagem Java Card [Che00], parte da plataforma Java voltada ao desenvolvimento para smart cards e outros dispositivos com restrições quanto ao poder de processamento e armazenamento. A escolha de Java Card é justificada por ela ser uma das linguagens mais utilizadas na indústria smart card. Além disso, Java Card beneficia-se das vantagens de Java, como a segurança quanto a tipos, a ampla variedade de ferramentas disponíveis, dentre outras. Salienta-se que o desenvolvimento deste trabalho teve início com a pesquisa de aplicações Java Card na literatura e da identificação da estrutura geral que elas apresentavam. Desenvolveu-se então, como estudos de caso, uma aplicação de bilhetagem em transporte público e outra de carteira eletrônica. O resultado desses desenvolvimentos iniciais originou a primeira versão do método BSmart [GMD05, DGM06] e das suas ferramentas de apoio [DGM08], a saber o tradutor de código de B para Java Card e o gerador de refinamentos, conforme apre- 173 sentado na dissertação de mestrado [Gom07]. Destaca-se que o presente estudo é uma continuação do trabalho desenvolvido no mestrado. Na dissertação, alguns pontos do desenvolvimento ficaram sob a responsabilidade do software gerador de código, como a tradução dos tipos primitivos e inserção de métodos da classe applet na aplicação cartão. De outro modo, na tese, a responsabilidade do tradutor foi reduzida e direcionada para os refinamentos do método BSmart. Os tipos e chamadas a operações da API Java Card agora são tratados através de módulos inseridos na biblioteca KitSmart. No caso dos métodos da classe applet, a solução encontrada foi separar o desenvolvimento principal dos serviços do componente que modela o applet, esse último sendo um refinamento direto da classe applet que importa a máquina de serviço. O objetivo maior da tese é permitir, através do método BSmart, a geração da aplicação Java Card completa, composta pelo applet que fornece os serviços no lado cartão e por uma API para o acesso transparente aos serviços no cartão, possibilitando a correção de todo o processo através das abordagens delineadas na seção 9.1.1. Espera-se que o método BSmart e as suas ferramentas sejam o fundamento de um ambiente que possibilite a criação de aplicações Java Card confiáveis e seguras, proporcionando que estas sejam geradas com menos erros e menor probabilidade de falhas. Nas seções a seguir evidenciam-se as contribuições desta tese (seção 9.1), analisa-se a adequação do método B ao desenvolvimento formal de aplicações para Java Card (seção 9.2) e delineiam-se os desenvolvimentos futuros deste trabalho (seção 9.3). 9.1 Principais Contribuições da Tese Nesta seção, destacam-se as principais contribuições da tese, a saber, as ações voltadas a assegurar a correção do método BSmart, as ferramentas desenvolvidas, a biblioteca KitSmart e o estudo de caso do passaporte eletrônico. Destaca-se que parte das especificações e demais artefatos produzidos na tese, bem como as ferramentas implementadas, podem ser obtidas no sítio http://code.google.com/p/bsmart/. Nesse endereço também encontram-se as instruções de instalação e execução das ferramentas de software. 174 9.1.1 Correção do Método BSmart A tese enfatiza a correção do processo de desenvolvimento e geração de código com o método BSmart através de três abordagens principais, a saber: I. Formalização e verificação do desenvolvimento (especificação e refinamentos) requerido pelo método BSmart utilizando o próprio método B e a ferramenta de verificação provida pelo Atelier B; II. Especificação e verificação das bibliotecas do KitSmart; III. Descrição das gramáticas das linguagens B0 e Java e o mapeamento da tradução entre cada elemento dessas linguagens através de regras de reescrita de termos em ASF+SDF. Observa-se que a verificação do método assegura a correção do processo de especificação e refinamento e da adaptação de interface entre os componentes Java e Java Card. Esta parte da tese, apresentada no capítulo 4, utilizou-se de um desenvolvimento genérico em B, no qual, inicialmente, foram representados os elementos significativos da especificação relacionados à mudança de estado e à verificação. Esses elementos foram inseridos em máquinas de contexto na forma de constantes funcionais, contendo propriedades adicionais que definiam os aspectos a ser verificados para a sua correção, tal como a relação das entradas de uma operação com a sua pré-condição e, posteriormente, se a mudança de estado a partir desse novo valor respeita as restrições impostas pelo invariante. A partir desses componentes básicos foram então descritas e verificadas as máquinas e refinamentos componentes do método BSmart. Ressalta-se que as bibliotecas do KitSmart também fornecem um auxílio ao processo de verificação do método, prevenindo a utilização incorreta dos componentes da API Java Card e possibilitando o uso adequado de tipos na especificação. É importante enfatizar os diversos componentes desta biblioteca que foram desenvolvidos ou aprimorados no decorrer desta tese. Dentre eles, destacam-se as máquinas de tipos básicos short, byte, int e boolean da linguagem Java, máquinas de biblioteca que podem ser utilizadas na implementação B para encapsular o estado de valores inteiros e sequências e os 93 componentes da biblioteca Java Card 2.2.2. Esse último ramo da biblioteca foi concluído no trabalho de mestrado de Simone Santos [San12]. Insta frisar que a biblioteca pode ser reutilizada em diversos projetos, mesmo sem vínculo com o método BSmart. A especificação da tradução para Java Card em ASF+SDF, apesar de não se constituir em uma prova da tradução, fornece, potencialmente, uma maior confiança em sua correção, além de um elemento de documentação útil para a implementação das ferramentas, bem como 175 para futuros usuários e desenvolvedores. Ressalta-se que esse processo possibilita a verificação através da inspeção de cada elemento e da simulação da transformação entre as linguagens através da ferramenta Meta-Environment. É importante enfatizar que o modelo em ASF+SDF não se configura em uma prova formal da tradução. Para tanto, seria necessário a descrição formal da semântica de ambas as linguagens e da transformação entre elas. Optou-se pela solução adotada ao se verificar que o tempo disponível para a realização desta tese, com o atendimento dos seus outros objetivos, não seria suficiente para concluir a tarefa de especificação semântica. Por outro lado, o trabalho desenvolvido pode servir como uma referência inicial para uma futura criação do seu modelo formal. 9.1.2 Suporte de Ferramentas ao Método O suporte de ferramentas ao método BSmart foi outra importante contribuição da tese. Destaca-se o emprego da biblioteca BCompiler [Cle12b] ao desenvolvimento dos programas, fornecendo a verificação sintática e de tipos, assim como a geração da árvore de parser, a partir da qual torna-se possível acessar e manipular o código B. Outro benefício do uso dessa API foi compatibilizar o BSmart com a linguagem B fornecida para a ferramenta Atelier B. Na tese, desenvolveu-se a biblioteca AMNPrinter que usa o BCompiler para possibilitar a geração de código de B para outro módulo B e para Java ou Java Card. Através desse componente, contribui-se com três programas, a saber, um gerador de aplicação host (host_gen), outro para automatizar a criação do refinamento full function e da sua máquina de contexto (ff_gen) e um tradutor de B para Java e JavaCard (b2java_gen). Observa-se que o software pode ser executado em linha de comando ou ser integrado à interface gráfica fornecida pela ferramenta Atelier B. Deve-se preferir a segunda forma, tendo em vista tornar o desenvolvimento mais ágil e possibilitar o uso de toda a gama de ferramentas que o ambiente oferece. 9.1.3 Estudo de Caso Cumpre salientar que o estudo de caso do passaporte eletrônico do capítulo 7 foi um importante auxílio à validação do método, da biblioteca KitSmart e do seu suporte de ferramentas. No primeiro caso, seguiu-se os passos delineados no método para especificar e verificar corretamente, até a implementação B, os métodos relacionados aos protocolos fundamentais do passaporte (Passive Authentication e Basic Access Control). 176 O estudo de caso possibilitou o teste e a melhoria das ferramentas, em especial o gerador de código, tendo em vista que boa parte da notação e das instruções para implementação B foram utilizadas. Para as demais ferramentas, que recebem como entrada uma máquina ou refinamento, também foram utilizadas dezenas de módulos B que possibilitaram testá-las com diversas combinações de elementos, tais como, substituições, predicados, expressões e as diferentes formas de definição de operações. A adoção da ferramenta em outros projetos também será importante para o seu aprimoramento e correção de eventuais erros. 9.2 O Método Formal B aplicado ao Desenvolvimento Smart Card Nesta seção tecemos algumas considerações sobre a experiência do uso do método formal B neste trabalho, evidenciando as suas vantagens a este tipo de desenvolvimento, assim como algumas situações que tiveram que ser contornadas devido a limitações ou características particulares do método em sua versão clássica. Inicialmente, evidencia-se que o método B, apesar das limitações listadas nesta seção, mostra-se adequado à especificação e refinamento de aplicações Java Card. Para tanto, deve-se destacar o fato do ambiente Java Card fornecer uma linguagem bem mais restrita se comparado ao Java tradicional, o que resulta em desenvolvimento formal mais conciso, concentrando-se em menos aspectos de linguagem e do ambiente de execução. Observa-se ainda que diversos componentes da linguagem de implementação B encontram equivalência direta com elementos da linguagem Java Card, dentre os quais pode-se elencar as instruções, os tipos de dados inteiros (nesse caso com o suporte dos módulos do KitSmart), as diversas expressões e, de certo modo, a composição de máquinas, que pode ser reproduzida por meio do relacionamento entre classes em Java Card. Há que se destacar a estrutura rígida do refinamento em B como causa de algumas dificuldades identificadas durante o processo de desenvolvimento. O método B exige que as operações mantenham a sua interface, tal qual estabelecida no modelo abstrato, durante toda a cadeia de refinamentos. No caso de Java Card, em sua versão mais usual, na qual a comunicação entre as aplicações é feita por meio do protocolo APDU, a interface fornecida pela especificação concreta pode ser diferente daquela encontrada no componente abstrato (tipos e quantidade de parâmetros), tendo em vista que a recepção e envio de dados é feita através do buffer apdu. Desse modo, nesse trabalho foi preciso criar como alternativa à verificação da mudança de interface através de uma refinamento indireto, separado do desenvolvimento principal, no qual 177 o componente abstrato refinado inclui a máquina concreta, relacionam-se seus estados, e cada mudança de estado abstrata é feita no corpo da operação através de chamadas às operações concretas. Observa-se que a restrição discutida anteriormente foi resolvida em Event-B, no qual uma operação refinada pode modificar a interface, desde que faça referencia à operação abstrata que está sendo refinada em uma cláusula no corpo da operação. Destaca-se que em Event-B as operações também podem ser decompostas em mais de uma operação, ou o contrário. Infelizmente, o método B tradicional não foi atualizado para incluir a flexibilidade de refinamento apresentada por Event-B. Durante o desenvolvimento do trabalho também percebeu-se que alguns recursos comumente encontrados em linguagens de programação poderiam ser parte da linguagem fornecida pela implementação B, tais como a sobrecarga de operações e um modelo de exceções. No primeiro caso, ao desenvolver o KitSmart, as operações sobrecarregadas foram simplesmente renomeadas para permitir a referência às suas diversas formas. O envio ao cliente de um valor (código de status) correspondente a uma situação de erro que pode ser prevista é modelado através das máquinas do KitSmart. Conforme mencionado nos trabalhos relacionados, Burdy e Requet [BR03] propuseram um modelo teórico, juntamente com as obrigações de prova a serem verificadas, para adicionar cláusulas de especificação de exceções e instruções de controle de fluxo. No entanto, nenhuma dessas construções foi incorporada ao método ou a alguma ferramenta que o implemente. Cumpre observar a restrição ao suporte a tipos abstratos de dados, cuja representação em B limita-se a conjuntos e estruturas (struct). Desse modo, não há como tipar um elemento de um módulo como sendo composto por dados e operações (como uma outra máquina em B). Assim, a tarefa de fornecer um tipo abstrato a uma variável ou constante torna-se trabalhosa ou, no caso das soluções oferecidas nativamente pelo B, não refletem em sua totalidade a semântica encontrada na linguagem Java. Apesar das dificuldades supracitadas, ressalta-se como vantagens do método B o seu suporte de ferramentas, dentre as quais destacam-se o Atelier B e o Pro-B, e a relativa facilidade no aprendizado e desenvolvimento com o formalismo. No entanto, visando simplificar o trabalho do usuário, deve-se aumentar a automatização empregada ao método, bem como proceder com a criação de um guia de melhores práticas que devem ser seguidas na especificação e refinamento com o método. Destaca-se que deve ser realizado um estudo aprofundado do método formal Event-B e a sua adequação à especificação de aspectos do sistema smart card, de modo que o método B 178 continue sendo utilizado com o foco em desenvolver o código da aplicação Java Card principal e Event-B na especificação e verificação dos aspectos relacionados a comunicação entre os componentes cliente e applet. 9.3 9.3.1 Trabalhos Futuros Método BSmart Com relação ao método, conforme discutido na análise final do capítulo 4, identifica-se como melhoria mais evidente a especificação do componente Java Card Runtime Environment (JCRE), responsável por controlar o estado global da aplicação, o que compreende a troca de pacotes de dados APDU entre as aplicações host e cartão e o tratamento de erros (exceções) na aplicação cartão. Nesse caso, pretende-se ampliar a verificação de todo o processo de interação entre os componentes da aplicação Java Card, que pode ser especificada utilizando-se o formalismo Event-B. A extensão do método para uma gama maior de aplicações deve ser também explorada. Inicialmente, deve-se pesquisar em maior profundidade o Java Card 3.0, e verificar de que forma o método pode ser ampliado para atendê-lo. Cumpre evidenciar que a expansão do método deve ser acompanhada da evolução das ferramentas e da biblioteca KitSmart. No caso do KitSmart, ao se construir um modelo do JCRE, deve-se destinar especial atenção à melhoria do tratamento de aspectos internos às aplicações, como o controle de exceção e de trechos de código sob transação. 9.3.2 Ferramentas Salienta-se que as ferramentas ainda serão aprimoradas, com destaque para a continuidade dos testes e a adição de elementos de interface gráfica para permitir o melhor controle das opções que elas oferecem. O teste das ferramentas deve ser enfatizado através da continuidade do desenvolvimento do estudo de caso do passaporte eletrônico, bem como por outros estudos de caso disponíveis na literatura ou que venham a ser desenvolvidos. Neste sentido, salienta-se que a continuidade do KitSmart irá fornecer um aparato de máquinas e classes Java Card correspondentes, que podem ser traduzidas a partir da ferramenta geradora de código. Atualmente, o controle das ferramentas é feito através de parâmetros fornecidos no mo- 179 mento anterior à sua execução. Ao conectar as ferramentas à interface gráfica do Atelier B, alguns desses parâmetros são fornecidos internamente (sem a intervenção direta do usuário) através de um arquivo de configuração. No entanto, é interessante fornecer esse controle ao usuário, através de itens de interface gráfica, tais como janelas de diálogo, dessa forma permitindo uma maior customização antes de iniciar cada execução. Deve-se acrescentar também a possibilidade do usuário inserir suas próprias bibliotecas à ferramenta. Na versão atual, podem ser referenciados todos os módulos do projeto do usuário e os componentes do KitSmart. Por outro lado, projetos e códigos B externos ainda não podem ser acessados. A adição de novos componentes se faz necessária como forma de aumentar a automação de todo o processo, tendo em vista torná-lo mais atrativo ao desenvolvedor. Nesse ponto, deve-se implementar: • Um gerador do applet Java Card que irá gerar a implementação JC_Applet.imp, contendo uma implementação padrão para o seu método process, que irá receber o APDU e chamar as operações do módulo de serviço. • Um assistente para a criação dos módulos necessários à verificação da conformidade entre os modelos abstratos e concretos. 9.3.3 Otimização de Código É importante observar que a otimização aplicada aos refinamentos do método e ao código gerado é uma questão importante para o desenvolvimento smart card que ainda não foi tratada em profundidade no presente estudo. Conforme discutido no capítulo 8, nos trabalhos de Requet e Bossu [RB00], Bert [B+ 03] e Voisinet [Voi02], a geração de código otimizada para as linguagens C e Java foi estudada no âmbito do projeto B with Optimized Memory (BOM), tendo em vista a instalação das aplicações desenvolvidas em smart cards. Na continuação deste trabalho, deve-se pesquisar otimizações que possam ser inseridas diretamente sob forma de refinamento ao método BSmart, tais como a busca por padrões de refinamento de instruções e otimizações na declaração de variáveis de estado e na definição de variáveis locais. Observa-se que essas abordagens podem ser inicialmente descritas como um guia, e, nos casos onde for possível, inseridos em ferramenta. Nas situações em que não seja adequado introduzir em B, as otimizações serão acrescentadas durante a fase de tradução, utilizando-se as ideias dos trabalhos descritos anteriormente. Nesse sentido, identifica-se como otimização já aplicada ao tradutor, a criação de objetos persistentes dentro do método construtor da aplicação Java Card. Deve-se também buscar soluções para minimizar a criação de classes, 180 que atualmente são geradas a partir dos módulos de implementação (principal e módulos importados) e dos conjuntos adiados e enumerados. 9.3.4 KitSmart Verificam-se diversas atividades que visam aprimorar o KitSmart, dentre elas, é possível destacar: I. Revisar e, eventualmente, corrigir ou modificar os módulos B para tarefas que podem ser úteis ao desenvolvedor Java Card, tais como, internacionalização, manipulação de valores numéricos reais, etc. Devido a esses modelos originarem classes correspondentes em Java Card, deve-se refiná-los até o módulo de implementação e gerar a classe correspondente. Essas classes podem ser utilizadas em diferentes desenvolvimentos Java Card, contribuindo, portanto, para diminuir o retrabalho em verificação, tradução e compilação. A tarefa de geração de código deve ser feita com a ferramenta tradutora do BSmart, dessa forma tornando o desenvolvimento mais ágil e contribuindo para testar esse componente de software. II. Pesquisar a melhor forma de se representar a tipagem com tipos abstratos de dados, levando em consideração a semântica de Java Card e o aspecto de verificação em B. Atualmente, uma classe é representada sob a forma de um conjunto de mesmo nome da classe em Java. Uma subclasse em uma hierarquia de herança é tratada como um subconjunto do conjunto que representa a superclasse. Outra abordagem tentada foi modelar uma classe através de um struct em B, que corresponde a sua definição estática, e da instância de classe através de um record, representando o componente dinâmico, ou o objeto Java. As limitações dessa abordagem dizem respeito ao par struct/record lidar apenas com estado, sem permitir a definição de funções, e a falhas no provador do Atelier B tentar verificar corretamente as definições para uma estrutura. Uma primeira abordagem consiste em identificar a causa e solucionar esta falha, tendo em vista possibilitar o uso corretos das estruturas na representação de tipos. III. Estudar toda a API Java Card para a versão Java Card 3.0 classic, evolução da API 2.2.2. Posteriormente, adaptar os módulos já desenvolvidos e criar novos modelos de acordo de modo a especificar a totalidade da API classic. IV. Estudar a API Java Card 3.0 connected, verificando-se a viabilidade de fornecer a especificação para essa versão da API, que possui uma gama maior de bibliotecas e recursos 181 encontrados na linguagem Java, tais como threads múltiplos, Strings, o tipo int, enum, generics e coleta de lixo. V. Melhorar a abordagem para a especificação de exceções. Tendo em vista que o controle do lançamento de exceções é feito internamente pelo ambiente de execução (JCRE), inserir a modelagem do tratamento de situações de erro no modelo do JCRE e da sua interação com as aplicações componentes do ambiente Java Card. Outro aspecto a ser levado em consideração é o controle de transações, um tópico importante ao desenvolvimento Java Card, tendo em vista evitar que dados sob transação tornem-se inconsistentes após uma interrupção abrupta inesperada, como a retirada do cartão do leitor antes de um operação ser completada. 9.3.5 Estudo de Caso O estudo de caso do passaporte eletrônico foi utilizado na validação do método, das bibliotecas e ferramentas. Com esse propósito, especificou-se as máquinas e refinamentos necessários à implementação dos protocolos Basic Access Control (BAC) e Passive Authentication (PA), fundamentais para a autenticação e o acesso inicial aos arquivos de dados básicos do portador do documento armazenados no chip. Como trabalho futuro espera-se desenvolver todos os recursos necessários requeridos pelos demais protocolos, a saber o Active Authentication (AA) e o Extended Access Control (EAC). Outro ponto a ser explorado diz respeito à especificação de parte da aplicação cliente. Nesse sentido, deve-se identificar quais componentes dessa aplicação podem ser inseridos, de modo a complementar a especificação e melhorar o aspecto de verificação global da aplicação. Com essa abordagem, torna-se possível refinar o cliente até a sua implementação B e gerar o código dessa parte da aplicação para Java. 182 Referências [ABH+ 10] Jean-Raymond Abrial, Michael Butler, Stefan Hallerstede, Thai Son Hoang, Farhad Mehta, and Laurent Voisin. Rodin: an open toolset for modelling and reasoning in Event-B. STTT, 12(6):447–466, 2010. [Abr96] J.-R. Abrial. The B-Book: Assigning Programs to Meanings. Cambridge University Press, 1996. [Abr10] J.-R. Abrial. Modeling in Event-B: System and Software Engineering. Cambridge University Press, 2010. [B+ 03] Didier Bert et al. Adaptable translator of B specifications to embedded C programs. In FME, volume 2805 of LNCS, pages 94–113, September 2003. [BBFM99] Patrick Behm, Paul Benoit, Alain Faivre, and Jean-Marc Meynadier. Météor: A Successful Application of B in a Large Project. In Formal Methods 99, volume 1708 of LNCS, 1999. [BBK+ 07] Emilie Balland, Paul Brauner, Radu Kopetz, Pierre-Etienne Moreau, and Antoine Reilles. Tom: piggybacking rewriting on Java. In Proceedings of the 18th international conference on Term rewriting and applications (RTA’07), pages 36–47, Berlin, 2007. Springer. [BH94] J. P. Bowen and M. G. Hinchey. Seven more myths of formal methods: Dispelling industrial prejudices. In M. Naftalin, T. Denvir, and M. Bertran, editors, FME’94: Industrial Benefit of Formal Methods, pages 105–117. Springer, Berlin, 1994. [BKV07] Mark Van Den Brand, Paul Klint, and Jurgen Vinju. The syntax definition formalism SDF, 2007. Available on: http://homepages.cwi.nl/˜daybuild/dailybooks/syntax/sdf/sdf.html. [BKV08] Mark Van Den Brand, Paul Klint, and Jurgen Vinju. The language specification formalism ASF+SDF, 2008. Available on: http://homepages.cwi.nl/˜daybuild/daily-books/extractiontransformation/asfsdf/asfsdf.html. [BP98] Richard Banach and M. Poppleton. Retrenchment: An engineering variation on refinement. In B’98: Proceedings of the Second International B Conference, pages 129–147, London, UK, 1998. Springer-Verlag. [BR03] Lilian Burdy and Antoine Requet. Extending B with control flow breaks. In Proceedings of the 3rd international conference on Formal specification and development in Z and B, ZB’03, pages 513–527, Berlin, 2003. Springer. 183 [BY07] Michael Butler and Divakar Yadav. An Incremental Development of the Mondex System in Event-B. Formal Aspects of Computing, 20(1):61–77, 2007. [CBR02] Ludovic Casset, Lilian Burdy, and Antoine Requet. Towards a full formal specification of the Java Card API. In International Conference on Dependable Systems and Networks (DSN’02), pages 51–56. IEEE Computer Society, 2002. [CEF11] CEF. Página Web da Caixa Econômica Federal. http://www.caixa.gov.br, 2011. Acesso em: 10 nov. 2011. [Cer11] Certisign. Página Web do Certisign. Disponível http://www.certisign.com.br/, 2011. Acesso em: 10 nov. 2011. [Che00] Zhiqun Chen. Java Card Technology for Smart Cards: Architecture and Programmer’s Guide. Addison Wesley, Boston, 2000. [Cle12a] Clearsy. Atelier B tool. http://www.atelierb.eu/, 2012. Acesso em: 25 jun. 2012. [Cle12b] Clearsy. B-Compiler project. http://www.tools.clearsy.com/wp1/?page_id=153, 2012. Acesso em: 25 jun. 2012. [Cle12c] Clearsy. ComenC tool. http://www.tools.clearsy.com/wp1/?page_id=59, 2012. Acesso em: 25 jun. 2012. [Com10] Common Criteria. The Common http://www.commoncriteriaportal.org/cc/, 2010. [CW96] Edmund M. Clarke and Jeannette M. Wing. Formal methods: State of the art and future directions. ACM Computing Surveys, 28(4):626–643, 1996. [DGM06] David Déharbe, Bruno Gomes, and Anamaria Moreira. Automation of Java Card component development using the B method. In ICECCS, pages 259–268. IEEE Comp. Soc., 2006. [DGM08] David Déharbe, Bruno Gomes, and Anamaria Moreira. BSmart: A Tool for the Development of Java Card Applications with the B Method. In ABZ, pages 351–352, 2008. [dJ12] Ministério da Justiça. Registro de Identidade Civil. http://portal.mj.gov.br/ric, 2012. Acesso em: 5 jan. 2012. [DLMP08] Frédéric Dadeau, Julien Lamboley, Thierry Moutet, and Marie-Laure Potet. A verifiable conformance relationship between smart card applets and B security models. In Proceedings of the 1st international conference on Abstract State Machines, B and Z (ABZ’08), pages 237–250, Berlin, 2008. Springer. [dM12] Ernesto Brasil de Matos. Uma ferramenta para geração de testes de unidade a partir de especificações B. Mestrado, Programa de Pós-graduação em Sistemas e Computação, UFRN, Natal, 2012. [DPT08] Frédéric Dadeau, Marie-Laure Potet, and Régis Tissot. A B formal framework for security developments in the domain of smart card applications. In IFIP 23rd International Information Security, pages 141–155, 2008. Disponível em: Criteria em: Portal. Disponível em: 184 [Dut06] Thiago Dutra. KitSmart: Um kit de tipos e estruturas de dados projetados com o método B para o desenvolvimento rigoroso em Java Card. Graduação, Departamento de Informática e Matemática Aplicada, Universidade Federal do Rio Grande do Norte, Natal, 2006. [EB10] Andy Edmunds and Michael Butler. Tool Support for Event-B Code Generation. Workshop On Tool Building in Formal Methods, ABZ 2010, Orford, CA, 2010. [Ecl10] Eclipse Foundation. Eclipse web site. www.eclipse.org. Last access: oct. 2010, 2010. [Eur12] Eurosmart. Eurosmart expects over 7 billion smart secure devices to be shipped in 2012. Technical report, Eurosmart, 2012. http://www.eurosmart.com/index.php/publications/market-overview.html. [FB07] Simon Fraser and Richard Banach. Configurable proof obligations in the Frog toolkit. In Software Engineering and Formal Methods, pages 361–370, London, UK, 2007. IEEE Computer Society. [FFW07] Leo Freitas, Zheng Fu, and Jim Woodcock. POSIX file store in Z/Eves: an experiment in the verified software repository. In Proceedings of the 12th IEEE International Conference on Engineering Complex Computer Systems, pages 3–14, Washington, DC, USA, 2007. IEEE Computer Society. [FJM04] Houda Fekih, Leila Jemmi, and Stephan Merz. Transformation des spécifications B en des diagrammes UML. In Proceedings of Approches Formelles dans l’Assistance au Développement de Logiciels (AFADL’04), Besançon, June 2004. [Fra08] Simon Fraser. Mechanized Support for Retrenchment. PhD in computer science, Faculty of Engineering and Physical Sciences of University of Manchester, Manchester, UK, 2008. [Gar06] Gary Leavens and Yoonsik Cheon. Design by Contract with JML. http://www.eecs.ucf.edu/˜leavens/JML/jmldbc.pdf, 2006. [GDMM10] Bruno Gomes, David Déharbe, Anamaria Moreira, and Katia Moraes. Applying the B Method for the Rigorous Development of Smart card Applications. In Abstract State Machines, Alloy, B and Z (ABZ 2010), pages 203–216, Orford, Canada, 2010. Springer. [Glo09] Global Platform. Global Platform Web site. http://www.globalplatform.org, 2009. [GMD05] Bruno Gomes, Anamaria Moreira, and David Déharbe. Developing Java Card applications with B. In Brazilian Symposium on Formal Methods (SBMF), pages 63–77, 2005. [GO09] Artur Gomes and Marcel Oliveira. Formal specification of a cardiac pacing system. In Proceedings of the 2nd World Congress on Formal Methods, FM’09, pages 692–707, Berlin, 2009. Springer. Available on: 185 [Gom07] Bruno Gomes. BSmart: Desenvolvimento Rigoroso de Aplicações Java Card com base no Método formal B. Master’s thesis, Universidade Federal do Rio Grande do Norte, Natal, 2007. [Gom09] Bruno Gomes. Rigorous Development of Smart card Applications with the B method. In Proceedings of the Doctoral Symposium of Formal Methods 2009, pages 32–38, Eindhoven, The Netherlands, 2009. Technische Universiteit Eindhoven. [HMLS09] C. A. R. Hoare, Jayadev Misra, Gary T. Leavens, and Natarajan Shankar. The Verified Software Initiative: A manifesto. ACM Computing Surveys, 41(4), 2009. [HNS+ 02] Uwe Hansmann, Martin Nicklous, Thomas Schäck, Achim Schneider, and Frank Seliger. Smart Card Application Development Using Java. Springer, Berlim, 2002. [HPP00] Wai-Ming Ho, Francois Pennaneac’h, and Noel Plouzeau. UMLAUT: A Framework for Weaving UML-Based Aspect-Oriented Designs. In Proceedings of the Technology of Object-Oriented Languages and Systems (TOOLS 33), page 324, Washington, DC, USA, 2000. IEEE Computer Society. [ICA06] ICAO. Machine Readable Travel Documents: Part I - Machine Readable Passports. Technical report, International Civil Aviation Organization, 2006. [ICA11] ICAO. Página Web da International Civil Aviation Organization. http://www2.icao.int/, 2011. Acesso em: 10 nov. 2011. [IL04] A. Idani and Y. Ledru. Object Oriented Concepts Identifications from Formal B Specifications. In FMICS, September 2004. [INR10] INRIA. The Coq Proof Assistant. http://coq.inria.fr/, 2010. [ITI11] ITI. Página Web do Instituto Nacional de Tecnologia da Informação. Disponível em: http://www.iti.gov.br/twiki/bin/view/Main/WebHome, 2011. Acesso em: 10 nov. 2011. [JJ05] Claude Jard and Thierry Jéron. TGV: theory, principles and algorithms: A tool for the automatic synthesis of conformance test cases for non-deterministic reactive systems. International Journal on Software Tools for Technology Transfer (STTT), 7(4):297–315, 2005. [JUn10] JUnit.org. Junit: Resources for test driven development. http://www.junit.org/, 2010. [KATP02] Pieter Koopman, Artem Alimarine, Jan Tretmans, and Rinus Plasmeijer. Gast: Generic Automated Software Testing. In 14th International Workshop on the Implementation of Functional Languages (IFL’02), number 2670 in LNCS, pages 84–100, Madrid, Spain, 2002. Springer. [Kli07] Paul Klint. Quick introduction to term rewriting, 2007. Available on: http://www.meta-environment.org/doc/books/extraction-transformation/termrewriting/term-rewriting.html. 186 [KvdSV09] Paul Klint, Tijs van der Storm, and Jurgen Vinju. Rascal: A domain specific language for source code analysis and manipulation. Source Code Analysis and Manipulation, IEEE International Workshop on, 0:168–177, 2009. [Lan00] Jean Louis Lanet. Are smart cards the ideal domain for applying formal methods? In ZB ’00: Proceedings of the First International Conference of B and Z Users on Formal Specification and Development in Z and B, pages 363–373, London, UK, 2000. Springer. [Lar03] Daniel Larsson. OCL Specifications for the Java Card API. Master’s thesis, School of Computer Science and Engineering, Göteborg University, 2003. [LBBP10] Michael Leuschel, Michael Butler, Jeans Bendisposto, and Daniel Plage. The ProB animator and model checker. http://www.stups.uniduesseldorf.de/ProB/index.php5/Main_Page, 2010. [LC06] Gary Leavens and Yoonsik Cheon. Design by contract with JML. Technical report, University of Central Florida, 2006. [LM04] Daniel Larsson and Wojciech Mostowski. Specifying Java Card API in OCL. In Peter H. Schmitt, editor, OCL 2.0 Workshop at UML 2003, volume 102C of ENTCS, pages 3–19. Elsevier, November 2004. [LS07] Thierry Lecomte and Thierry Servant. Formal methods in safety-critical railway systems. In 10th Brasilian Symposium on Formal Methods (SBMF2007), pages 1–, Ouro Preto, 2007. [MC10] Georges Mariano and Samuel Colin. Brillant: an open source platform for B. Workshop on Tool Building in Formal Methods, ABZ 2010, Orford, CA, 2010. [MdB01] Hugues Martin and Lydie du Bousquet. Automatic test generation for Java-Card applets. In JavaCard ’00: Revised Papers from the First International Workshop on Java on Smart Cards: Programming and Security, pages 121–136, London, UK, 2001. Springer. [MdMJD+ 08] Éberton Marinho, Valério de Medeiros Júnior, David Déharbe, Bruno Gomes, and Cláudia Tavares. A Ferramenta Batcave para a Verificação de Especificações Formais na Notação B. In XV Sessão de Ferramentas do XXII Simpósio Brasileiro de Engenharia de Software, pages 1–6, Campinas, 2008. [ME09] Meta-Environment.org. The ASF+SDF Meta-Environment, 2009. Available on: http://www.meta-environment.org. [MLF08] Hugo Daniel Macedo, Peter Gorm Larsen, and John S. Fitzgerald. Incremental development of a distributed real-time model of a cardiac pacing system using VDM. In Formal Methods 2008 (FM’08), pages 181–197, 2008. [Mor07] Katia Moraes. Geração Automática de API’s para o Desenvolvimento de Aplicações Cliente Java Card a partir da Especificação Formal. Graduação, Departamento de Informática e Matemática Aplicada, Universidade Federal do Rio Grande do Norte, Natal, 2007. 187 [MP01] H. Meijer and E. Poll. Towards a Full Formal Specification of the Java Card API. In Smart Card Prog. and Security, volume 2140 of LNCS, pages 165–178. Springer, 2001. [MP10] Wojciech Mostowski and Erik Poll. Electronic Passports in a Nutshell. Technical Report ICIS–R10004, Radboud University Nijmegen, June 2010. Available at https://pms.cs.ru.nl/iris-diglib/src/getContent.php?id=2010-MostowskiElectronicNutshell. [MT00] Stéphanie Motré and Corinne Téri. Using B Method to Formalize the Java Card Runtime Security Policy for a Common Criteria Evaluation. In 23rd National Information Systems Security Conference (NISSC 2000), pages 1–, 2000. [Mul10] MultOS. MultOS Web site. www.multos.com, 2010. [Net07] Plácido Neto. Java card modelling language (JCML): Definição e Implementação. Master’s thesis, Universidade Federal do Rio Grande do Norte, Natal, 2007. [NPW10] Tobias Nipkow, Lawrence C. Paulson, and Markus Wenzel. Isabelle/HOL: A proof Assistant for High-Order Logic. http://www.cl.cam.ac.uk/research/hvg/Isabelle/dist/Isabelle/doc/tutorial.pdf, 2010. [OMG10] OMG. Object Constraint Language. http://www.omg.org/spec/OCL/, 2010. [OOS11] OOSTDIJK, M. et. al. JMRTD Project Web site. http://jmrtd.org, 2011. Acesso em: 08 mar. 2011. [Ora11] Oracle. Java card platform specification, version http://java.sun.com/javacard/specs.html. Acesso em: jun. 2011, 2011. [Ora12] Oracle. Java card 3.0 platform specification. http://java.sun.com/javacard/3.0/specs.jsp. Acesso em: mar. 2012, 2012. [Ort01] Ed Ort. Developing a Java Card Applet. Disponível em: http://developers.sun.com/techtopics/mobility/javacard/articles/applet/, 2001. Acesso em: 1 dez. 2006. [Ort03a] Enrique C. Ortiz. An Introduction to Java Card Technology. Available on: http://java.sun.com/javacard/reference/techart/javacard1, 2003. [Ort03b] Enrique C. Ortiz. An Introduction to Java Card Technology. Disponível em: <http://developers.sun.com/techtopics/mobility/javacard/articles/javacard2>, 2003. Acesso em: 26 out. 2006. [PC/09] PC/SC Workgroup. PC/SC Workgroup Web site. http://www.pcscworkgroup.com, 2009. [Pol10] Polícia Federal. Passaporte http://www.dpf.gov.br/servicos/passaporte/passaporte-eletronico/, Acesso em: 15 fev. 2010. Disponível em: 2.2.2. Available on: Eletrônico. 2010. 188 [PvdBJ01] Erik Poll, Joachim van den Berg, and Bart Jacobs. Formal Specification of the Java Card API in JML: the APDU class. Computer Networks, 36(4):407–421, 2001. [RB00] Antoine Requet and Gaëlle Bossu. Embedded formally proved code in a smart card: Converting B to C. In ICFEM’00, pages 15–, York, UK, 2000. IEEE Computer Society. [RE03] Wolfgang Rankl and Wolfgang Effing. Smart Card Handbook. John Wiley, Baffins Lane, 3rd. edition, 2003. [San12] Simone Santos. KitSmart: Uma biblioteca de componentes para o desenvolvimento rigoroso de aplicações Java Card com o método B. Mestrado, Programa de Pós-graduação em Sistemas e Computação, UFRN, Natal, 2012. [SB06] Colin Snook and Michael Butler. UML-B: Formal modeling and design aided by UML. ACM Trans. Softw. Eng. Methodol., 15(1):92–122, 2006. [SB08] Colin Snook and Michael Butler. UML-B and Event-B: an integration of languages and tools. In Proceedings of the IASTED International Conference on Software Engineering (SE’08), pages 336–341, Anaheim, CA, USA, 2008. ACTA Press. [Sch01] Steve Schneider. The B-Method: An Introduction. Palgrave Macmillan, 2001. [SCW00] Susan Stepney, David Cooper, and Jim Woodcock. An electronic purse: Specification, refinement, and proof. Technical monograph PRG-126, Oxford University Computing Laboratory, jul 2000. [Ser07] Thierry Servant. Brama: A new graphic animation tool for B models. In B 2007: Formal Methods and Development in B, LNCS 4355, pages 274–276, Besançon, France, 2007. Springer. [SL99] Denis Sabatier and Pierre Lartigue. The use of the B formal method for the design and the validation of the transaction mechanism for smart card applications. In FM’99 - Formal Methods: World Congress on Formal Methods in the Development of Computing Systems, number 1708 in LNCS, pages 348–368, Toulose, France, 1999. Springer. [SM10] Sun Microsystems. Java card platform specification 2.2.2. Disponível em: <http://java.sun.com/javacard/specs.html>, 2010. Acesso em: 03 ago. 2010. [Sou09] Fernanda Souza. Geração de casos de teste a partir de especificações B. Master’s thesis, Universidade Federal do Rio Grande do Norte, Natal, 2009. [Spi92] J.M. Spivey. The Z Notation: a Reference Manual. Prentice-Hall International Series in Computer Science. Prentice Hall, 2nd edition, 1992. [Ter03] Terese. Term Rewriting Systems. Cambridge University Press, 1st edition, 2003. [THV02] B. Tatibouet, A. Hammad, and J. C. Voisinet. From Abstract B Specification to UML Class Diagrams. In ISSPIT, 2002. 189 [TRVH03] B. Tatibouet, A. Requet, J. C. Voisinet, and A. Hammad. Java Card Code Generation from B Specifications. In ICFEM, volume 2885 of LNCS, pages 306–318, 2003. [Vin05] Jurgen Vinju. Analysis and Transformation of Source Code by Parsing and Rewriting. PhD thesis, University of Amsterdam, 2005. [Voi02] J. C. Voisinet. JBtools: an experimental platform for the formal B method. In Principles and Practice of Programming, pages 137–139, Maynooth, 2002. NUI. [VSI10] VSI. The Verified Software Repository. http://vsr.sourceforge.net, 2010. [vWOF+ 05] Arjen van Weelden, Martijn Oostdijk, Lars Frantzen, Pieter W. M. Koopman, and Jan Tretmans. On-the-fly formal testing of a smart card applet. In Security and Privacy in the Age of Ubiquitous Computing, IFIP TC11 20th International Conference on Information Security (SEC’05), pages 565–576, Chiba, Japan, 2005. Springer. 190 APÊNDICE A -- Alguns componentes do KitSmart A.1 Tipos básicos - JShort MACHINE JShort CONCRETE_CONSTANTS JSHORT, MAXSHORT , MINSHORT , sum_short, subt_short, mult_short, div_short, mod_short, cast_short, equal_short, gt_short, gte_short PROPERTIES MAXSHORT ∈ Z ∧ MAXSHORT = 32767 ∧ MINSHORT ∈ Z ∧ MINSHORT = -32768 ∧ JSHORT = MINSHORT .. MAXSHORT ∧ sum_short ∈ JSHORT × JSHORT → JSHORT ∧ sum_short = λ ( n1 , n2 ) . ( n1 ∈ JSHORT ∧ n2 ∈ JSHORT ∧ n1 + n2 ∈ JSHORT | n1 + n2 ) ∧ subt_short ∈ JSHORT × JSHORT → JSHORT ∧ subt_short = λ ( n1 , n2 ) . ( n1 ∈ JSHORT ∧ n2 ∈ JSHORT ∧ n1 - n2 ∈ JSHORT | n1 - n2 ) ∧ mult_short ∈ JSHORT × JSHORT → JSHORT ∧ mult_short = λ ( n1 , n2 ) . ( n1 ∈ JSHORT ∧ n2 ∈ JSHORT ∧ n1 × n2 ∈ JSHORT | n1 × n2 ) ∧ div_short ∈ JSHORT × (JSHORT - {0}) → JSHORT ∧ div_short = λ ( n1 , n2 ) . ( n1 ∈ JSHORT ∧ n2 ∈ JSHORT ∧ ¬ ( n2 = 0 ) | n1 ÷ n2 ) ∧ mod_short ∈ JSHORT × (JSHORT - {0}) → JSHORT ∧ mod_short = λ ( n1, n2 ) . ( n1 ∈ JSHORT ∧ n2 ∈ JSHORT ∧ n2 6= 0 | n1 - n2 × (n1 ÷ n2)) ∧ cast_short ∈ Z → JSHORT ∧ ∀ ( s1 ) . ( s1 ∈ Z =⇒ (s1 = 0 =⇒ cast_short(s1) = 0) ∧ (s1 > 0 =⇒ cast_short(s1) = s1 mod 32767) ∧ (s1 < 0 =⇒ cast_short(s1) = -(-s1 mod 32767)) ) ∧ equal_short ∈ JSHORT × JSHORT → BOOL ∧ ∀ ( s1 , s2 ) . ( s1 ∈ JSHORT ∧ s2 ∈ JSHORT =⇒ ( ( s1 = s2 ) =⇒ equal_short ( s1 7→ s2 ) = TRUE ) ∧ ( ( s1 6= s2 ) =⇒ equal_short ( s1 7→ s2 ) = FALSE ) ) ∧ gt_short ∈ JSHORT × JSHORT → BOOL ∧ ∀ ( s1 , s2 ) . ( s1 ∈ JSHORT ∧ s2 ∈ JSHORT =⇒ ( ( s1 > s2 ) =⇒ gt_short ( s1 7→ s2 ) = TRUE ) ∧ ( ( s1 ≤ s2 ) =⇒ gt_short ( s1 7→ s2 ) = FALSE ) ) ∧ gte_short ∈ JSHORT × JSHORT → BOOL ∧ ∀ ( s1 , s2 ) . ( s1 ∈ JSHORT ∧ s2 ∈ JSHORT =⇒ ( ( s1 ≥ s2 ) =⇒ gte_short ( s1 7→ s2 ) = TRUE ) ∧ ( ( s1 < s2 ) =⇒ gte_short ( s1 7→ s2 ) = FALSE ) ) END 191 A.2 Tipos Básicos - JBoolean MACHINE JBoolean CONCRETE_CONSTANTS JBOOLEAN, or_boolean, lor_boolean, and_boolean, land_boolean, lnot_boolean, lxor_boolean PROPERTIES JBOOLEAN = BOOL ∧ lor_boolean ∈ JBOOLEAN × JBOOLEAN → JBOOLEAN ∧ lor_boolean = { ( TRUE 7→ TRUE ) 7→ TRUE , ( TRUE 7→ FALSE ) 7→ TRUE , ( FALSE 7→ TRUE ) 7→ TRUE , ( FALSE 7→ FALSE ) 7→ FALSE } ∧ land_boolean ∈ JBOOLEAN × JBOOLEAN → JBOOLEAN ∧ land_boolean = { ( TRUE 7→ TRUE ) 7→ TRUE , ( TRUE 7→ FALSE ) 7→ FALSE , ( FALSE 7→ TRUE ) 7→ FALSE , ( FALSE 7→ FALSE ) 7→ FALSE } ∧ lnot_boolean ∈ JBOOLEAN → JBOOLEAN ∧ lnot_boolean = { TRUE 7→ FALSE , FALSE 7→ TRUE } ∧ lxor_boolean ∈ JBOOLEAN × JBOOLEAN → JBOOLEAN ∧ lxor_boolean = { ( TRUE 7→ TRUE ) 7→ FALSE , ( TRUE 7→ FALSE ) 7→ TRUE , ( FALSE 7→ TRUE ) 7→ TRUE , ( FALSE 7→ FALSE ) 7→ FALSE } ∧ or_boolean ∈ JBOOLEAN × JBOOLEAN → JBOOLEAN ∧ ∀ ( b1 , b2 ) . ( b1 ∈ JBOOLEAN ∧ b2 ∈ JBOOLEAN ∧ b1 = TRUE =⇒ or_boolean ( b1 7→ b2 ) = TRUE ) ∧ ∀ ( b1 , b2 ) . ( b1 ∈ JBOOLEAN ∧ b2 ∈ JBOOLEAN ∧ b1 = FALSE ∧ b2 = TRUE =⇒ or_boolean ( b1 7→ b2 ) = TRUE ) ∧ ∀ ( b1 , b2 ) . ( b1 ∈ JBOOLEAN ∧ b2 ∈ JBOOLEAN ∧ b1 = FALSE ∧ b2 = FALSE =⇒ or_boolean ( b1 7→ b2 ) = FALSE ) ∧ and_boolean ∈ JBOOLEAN × JBOOLEAN → JBOOLEAN ∧ ∀ ( b1 , b2 ) . ( b1 ∈ JBOOLEAN ∧ b2 ∈ JBOOLEAN ∧ b1 = FALSE =⇒ and_boolean ( b1 7→ b2 ) = FALSE ) ∧ ∀ ( b1 , b2 ) . ( b1 ∈ JBOOLEAN ∧ b2 ∈ JBOOLEAN ∧ b1 = TRUE ∧ b2 = FALSE =⇒ and_boolean ( b1 7→ b2 ) = FALSE ) ∧ ∀ ( b1 , b2 ) . ( b1 ∈ JBOOLEAN ∧ b2 ∈ JBOOLEAN ∧ b1 = TRUE ∧ b2 = TRUE =⇒ and_boolean ( b1 7→ b2 ) = TRUE ) ASSERTIONS lor_boolean ( TRUE 7→ TRUE ) = TRUE ; lor_boolean ( TRUE 7→ FALSE ) = TRUE ; lor_boolean ( FALSE 7→ TRUE ) = TRUE ; lor_boolean ( FALSE 7→ FALSE ) = FALSE ; land_boolean ( TRUE 7→ TRUE ) = TRUE ; land_boolean ( TRUE 7→ FALSE) = FALSE ; land_boolean ( FALSE 7→ TRUE ) = FALSE ; land_boolean ( FALSE 7→ FALSE ) = FALSE ; lnot_boolean (TRUE) = FALSE ; lnot_boolean (FALSE) = TRUE ; lxor_boolean ( TRUE 7→ TRUE ) = FALSE ; lxor_boolean ( TRUE 7→ FALSE ) = TRUE ; lxor_boolean ( FALSE 7→ TRUE ) = TRUE ; lxor_boolean ( FALSE 7→ FALSE ) = FALSE ; or_boolean ( TRUE 7→ TRUE ) = TRUE ; or_boolean ( TRUE 7→ FALSE ) = TRUE ; or_boolean ( FALSE 7→ TRUE ) = TRUE ; or_boolean ( FALSE 7→ FALSE ) = FALSE ; and_boolean ( TRUE 7→ TRUE ) = TRUE ; and_boolean ( TRUE 7→ FALSE) = FALSE ; and_boolean ( FALSE 7→ TRUE ) = FALSE ; and_boolean ( FALSE 7→ FALSE ) = FALSE END 192 A.3 API Java Card - APDU MACHINE Apdu EXTENDS Object SEES JShort, JByte, Apdu_Properties, ISO7816 INCLUDES Util.Util CONCRETE_VARIABLES /* The current state of data processing */ state, /* The APDU buffer array */ buffer, /* The length of the incoming data field of the command */ lc, /*The expected length of the response (if any) */ le, /* The effective length of the response (if any) */ lr INVARIANT state ∈ STATE_INITIAL .. STATE_FULL_OUTGOING ∧ buffer ∈ seq1(JBYTE) ∧ size(buffer) > 0 ∧ size(buffer) ≤ 133 ∧ lc ∈ JSHORT ∧ lr ∈ JSHORT ∧ le ∈ JSHORT ∧ le ≥ 0 ∧ le ≤ 256 INITIALISATION /*Initializing 133 bytes*/ buffer := [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] || state := STATE_INITIAL || lc, le, lr := 0, 0, 0 OPERATIONS result ← getBuffer = BEGIN result := buffer END; CLA ← getCLAChannel = /*throws SecurityException*/ BEGIN CLA :∈ cast_byte(0) .. cast_byte(19) END; apdu ← getCurrentAPDU = /*throws SecurityException*/ BEGIN apdu :∈ APDU /* SecurityException if the current context is not the context of the currently selected applet instance or this method was not called, directly or indirectly, from the applet’s process method (called directly by the Java Card runtime environment), or the method is called during applet installation or deletion. */ END; apdu ← getCurrentAPDUBuffer = /*throws SecurityException*/ BEGIN apdu := buffer /* SecurityException if the current context is not the context of the currently selected applet instance or this method was not called, directly or indirectly, from the applet’s process method (called directly by the Java Card runtime environment), or the method is called during applet installation or deletion */ END; result ← getCurrentState = BEGIN result := state END; blocksize ← getInBlockSize = BEGIN blocksize : (blocksize ∈ JSHORT ∧ blocksize ≥ 0) END; length ← getIncomingLength = /* throws APDUException */ CHOICE length := lc OR length := 0 /* if no incoming data */ /* APDUException.ILLEGAL_USE if setIncomingAndReceive() not called or if setOutgoing() or setOutgoingNoChaining() previously invoked */ END; 193 nad ← getNAD = BEGIN nad :∈ JBYTE END; cdata ← getOffsetCdata = /*throws: APDUException*/ CHOICE cdata := cast_byte(5) /*when Lc is 1 byte*/ OR cdata := cast_byte(7) /*when Lc is 3 bytes*/ /* APDUException.ILLEGAL_USE if setIncomingAndReceive() not called or if setOutgoing() or setOutgoingNoChaining() previously invoked*/ END; blocksize ← getOutBlockSize = BEGIN blocksize : (blocksize ∈ JSHORT ∧ blocksize ≥ 0) END; protocol ← getProtocol = CHOICE protocol := PROTOCOL_TO OR protocol := PROTOCOL_T1 END; result ← isCommandChainingCLA = CHOICE result := TRUE /* this APDU is not the last APDU of a command chain */ OR result := FALSE END; result ← isISOInterindustryCLA = CHOICE result := TRUE OR result := FALSE END; result ← isSecureMessagingCLA = CHOICE result := TRUE OR result := FALSE END; result ← receiveBytes(bOff) = /* throws APDUException */ PRE bOff ∈ JSHORT ∧ bOff ≥ 0 ∧ bOff < BUFFER_LENGTH ∧ size(buffer) ≥ 4 ∧ state = STATE_PARTIAL_INCOMING /*APDUException if setIncomingAndReceive() not called or if setOutgoing() or setOutgoingNoChaining() previously invoked*/ /*APDUException if not enough buffer space for incoming block size*/ /*APDUException on I/O error.*/ /*APDUException if T=1 protocol is in use and the CAD sends in an ABORT S-Block command to abort the data transfer*/ THEN CHOICE state := STATE_PARTIAL_INCOMING OR state := STATE_FULL_INCOMING END || result : (result ∈ JSHORT ∧ result ≥ 0 ∧ result ≤ (buffer)(OFFSET_LC) sum_short(result,bOff) ≤ BUFFER_LENGTH) END; ∧ 194 sendBytes(bOff, len) = /* throws APDUException */ PRE /* the offset into APDU buffer */ bOff ∈ JSHORT ∧ /*APDUException if ’bOff’ is negative*/ bOff ≥ 0 ∧ /* the length of the data in bytes to send */ len ∈ JSHORT ∧ /*APDUException if ’len’ is negative */ len ≥ 0 ∧ /*APDUException or ’bOff+len’ exceeds the buffer size*/ sum_short(bOff,len) ≤ BUFFER_LENGTH ∧ len ≤ lr ∧ state = STATE_OUTGOING_LENGTH_KNOWN /* APDUException if setOutgoingLength() not called or setOutgoingAndSend() previously invoked or response byte count exceeded or if APDUException.NO_T0_GETRESPONSE or APDUException.NO_T0_REISSUE or APDUException.T1_IFD_ABORT previously thrown*/ /* APDUException on I/O error*/ /* APDUException.NO_T0_GETRESPONSE if T=0 protocol is in use and the CAD does not respond to (ISO7816.SW_BYTES_REMAINING_00+count) response status with GET RESPONSE command on the same origin logical channel number as that of the current APDU command*/ /*APDUException.NO_T0_REISSUE if T=0 protocol is in use and the CAD does not respond to (ISO7816.SW_CORRECT_LENGTH_00+count) response status by re-issuing same APDU command on the same origin logical channel number as that of the current APDU command with the corrected length*/ /*APDUException.T1_IFD_ABORT if T=1 protocol is in use and the CAD sends in an ABORT S-Block command to abort the data transfer*/ THEN CHOICE state := STATE_PARTIAL_OUTGOING OR state := STATE_FULL_OUTGOING END || lr := subt_short(lr, len) END; sendBytesLong(outData ,bOff ,len) = /* throws APDUException, SecurityException */ PRE /* the source data byte array */ outData ∈ seq(JBYTE) ∧ /* the offset into outData array */ bOff ∈ JSHORT ∧ bOff ≥ 0 ∧ /* the byte length of the data to send */ len ∈ JSHORT ∧ len ≥ 0 ∧ len ≤ lr ∧ state = STATE_OUTGOING_LENGTH_KNOWN /*SecurityException if the ’outData’ array is not accessible in the caller’s context*/ /*APDUException.ILLEGAL_USE if setOutgoingLength() not called or setOutgoingAndSend() previously invoked or response byte count exceeded or if APDUException.NO_T0_GETRESPONSE or APDUException.NO_T0_REISSUE or APDUException.NO_T0_REISSUE previously thrown*/ /* APDUException.IO_ERROR on I/O error*/ /* APDUException.NO_T0_GETRESPONSE if T=0 protocol is in use and CAD does not respond to (ISO7816.SW_BYTES_REMAINING_00+count) response status with GET RESPONSE command on the same origin logical channel number as that of the current APDU command */ /* APDUException.T1_IFD_ABORT if T=1 protocol is in use and the CAD sends in an ABORT S-Block command to abort the data transfer */ THEN CHOICE state := STATE_PARTIAL_OUTGOING OR state := STATE_FULL_OUTGOING END || lr := subt_short(lr, len) END; result ← setIncomingAndReceive = /* throws APDUException */ /*APDUException if setIncomingAndReceive() already invoked or if setOutgoing() or setOutgoingNoChaining() previously invoked*/ /* APDUException on I/O error*/ /* APDUException if T=1 protocol is in use and the CAD sends in an ABORT S-Block command to abort the data transfer*/ SELECT state = STATE_INITIAL ∧ size(buffer) ≥ 4 THEN CHOICE state := STATE_PARTIAL_INCOMING OR state := STATE_FULL_INCOMING END || result :(result ∈ JSHORT ∧ result ≥ 0 ∧ result ≤ buffer(OFFSET_LC)) END; 195 result ← setOutgoing = /* throws APDUException */ PRE (state = STATE_INITIAL ∨ state = STATE_PARTIAL_INCOMING ∨ state = STATE_FULL_INCOMING) /* APDUException if this method, or setOutgoingNoChaining() method already invoked */ /* APDUException I/O error */ THEN state := STATE_OUTGOING || result := le END; setOutgoingAndSend(bOff ,len) = /* throws: APDUException */ PRE /* the offset into APDU buffer - APDUException if negative */ bOff ∈ JSHORT ∧ bOff ≥ 0 ∧ /* length of the data to send - APDUException if negative */ len ∈ JSHORT ∧ len ≥ 0 ∧ /*APDUException if ’len’ is greater than 256 and the currently selected applet does not implement the javacardx.apdu.ExtendedLength interface*/ len ≤ 256 ∧ (state = STATE_INITIAL ∨ state = STATE_PARTIAL_INCOMING ∨ state = STATE_FULL_INCOMING) /*APDUException.IO_ERROR on I/O error*/ THEN state := STATE_FULL_OUTGOING || lr :∈ JSHORT END; setOutgoingLength(len) = /* throws APDUException */ PRE len ∈ JSHORT ∧ /*APDUException if ’len’ is negative*/ len ≥ 0 ∧ /*APDUException if ’len’ is greater than 256 and the currently selected applet does not implement the javacardx.apdu.ExtendedLength interface*/ len ≤ 256 ∧ state = STATE_OUTGOING /* APDUException if setOutgoing() or setOutgoingNoChaining() not called or if setOutgoingAndSend() already invoked, or this method already invoked */ /* APDUException I/O error */ THEN state := STATE_OUTGOING_LENGTH_KNOWN || lr := len END; result ← setOutgoingNoChaining = PRE (state = STATE_INITIAL ∨ state = STATE_PARTIAL_INCOMING ∨ state = STATE_FULL_INCOMING) /* APDUException if this method, or setOutgoing() method already invoked*/ /* APDUException I/O error */ THEN state := STATE_OUTGOING || result := le END; waitExtension = /* throws APDUException */ /* APDUException.ILLEGAL_USE if setOutgoingNoChaining() previously invoked*/ /* APDUException.IO_ERROR on I/O error*/ BEGIN skip END END 196 APÊNDICE B -- Especificação da Tradução para Java Card em ASF+SDF B.1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 B02Java.sdf module B−N o t a t i o n / B02Java i m p o r t s B−N o t a t i o n / C l a u s e imports java / syntax / Java exports c o n t e x t −f r e e s t a r t −s y m b o l s Implementation CompilationUnit c o n t e x t −f r e e s y n t a x t r d −bcomp ( I m p l e m e n t a t i o n ) −> C o m p i l a t i o n U n i t t r d −c l a u s e s ( I m p l e m e n t a t i o n C l a u s e ∗ ) t r d −c l a u s e ( I m p l e m e n t a t i o n C l a u s e + ) t r d −i m p o r t s ( { I m p o r t " , " } + ) t r d −s e e s ( { S e l e c t e d I d e n t " , " } + ) t r d −p r o m o t e s ( { S e l e c t e d I d e n t " , " } + ) t r d −s e t s ( { S e t " ; " } + ) t r d −v a l u a t i o n ( { V a l u a t i o n " ; " } + ) t r d −o p e r a t i o n s ( { O p e r a t i o n B 0 " ; " } + ) t r d −i n s t r u c t i o n s ({ I n s t r u c t i o n " ; " }+) trd −e l s i f s ( E l s i f B r a n c h ∗) trd −e l s i f ( ElsifBranch ) t r d −e l s e ( E l s e B r a n c h ? ) t r d −o r s ( OrBranch ∗ ) t r d −o r ( OrBranch ) t r d −c o n d i t i o n ( C o n d i t i o n ) t r d −t e r m ( Term ) t r d − l i s t −term −comma ( { Term " , " } + ) t r d − l i s t i d −t o −s t a t i c −c o n s t ( L i s t I d e n t C o m m a ) t r d − l i s t i d −t o −param− l i s t ( { I d e n t i f i e r " , " } + ) t r d − l i s t i d −t o −v a r −d e c l ( { I d e n t i f i e r " , " } + ) t r d −p r e d −t o −v a r −t y p e ( P r e d i c a t e ) t r d −p r e d −t o −c o n s t −t y p e ( P r e d i c a t e ) hiddens variables " $BImpl " [0 −9]∗ " $Imps1 + " " $Imp " [0 −9] " $ListInstBO " " $ B I n s t " [0 −9]∗ " $ B I n s t L " [0 −9]∗ " $ J S t a t " [0 −9]∗ " $JStat1 ∗" " $JStat2 ∗" " $ J S t a t 1 p +" " $ J S t a t 2 p +" " $JBlockSt1∗" " $JBlockSt2∗" " $JBlockSt3∗" −> −> −> −> −> −> −> −> −> −> −> −> −> −> Implementation { I m p o r t " , " }+ Import ListInstanciationB0 Instruction InstructionLevel1 Statement Statement∗ Statement∗ Statement+ Statement+ BlockStatement∗ BlockStatement∗ BlockStatement∗ −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> ClassBodyDeclaration∗ ClassBodyDeclaration+ ClassBodyDeclaration+ ClassBodyDeclaration+ ClassBodyDeclaration+ ClassBodyDeclaration+ ClassBodyDeclaration+ ClassBodyDeclaration+ BlockStatement+ BlockStatement∗ BlockStatement BlockStatement∗ SwitchBlockStatementGroup∗ SwitchBlockStatementGroup Primary Primary { P r i m a r y " , " }+ ClassBodyDeclaration+ { F o r m a l P a r a m e t e r " , " }+ BlockStatement+ ClassBodyDeclaration+ ClassBodyDeclaration+ 197 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 " $JBlockSt1+" " $JBlockSt2+" " $JBlockSt3+" " $ J B l o c k S t " [0 −9]∗ " $ J S w i t c h B l o c k " [0 −9]∗ " $JSwitchBlock0∗" " $JSwitchBlock1∗" " $BInsts1 +" " $BInsts2 +" " $Else ? " " $Else " " $ E l s i f " [0 −9]∗ " $Elsif0 ∗" " $ J E l s i f " [0 −9]∗ " $JE lsif0 ∗" " $JE lsif1 ∗" " $Either " " $Or " " $Or0 ∗ " " $Val " [0 −9]∗ " $Vals0 ∗" " $Vals1+" " $BTerm " [0 −9]∗ " $BTerm0∗ " " $BTermSimp " [0 −9]∗ " $LTermComma∗ " " $LTermComma0∗ " " $LTermComma1+ " " $LTermComma2+ " " $LTermCommaPar ? " " $LTermSimpComma+ " " $LTermSimpComma1+ " " $LTermSimpComma2+ " " $BCond " [0 −9]∗ " $ J P r E x p r " [0 −9]∗ " $JPrExpr0 ∗" " $JPrExpr1+" " $JPrExpr2+" " $BId " [0 −9]∗ " $ B I n t " [0 −9]∗ " $ P r e d " [0 −9]∗ " $LPredSemi + " [0 −9]∗ " $ I m p C l a u s e " [0 −9]∗ " $ I m p C l a u s e ∗ " [0 −9]∗ " $Header " " $ R e f i n e s " [0 −9]∗ " $ L i s t I d +" " $ListIdComma1 + " " $ListIdComma2 + " $ L i s t I d " [0 −9]∗ " $ S e l I d " [0 −9]∗ " $ L i s t S e l I d + " [0 −9]∗ " $CBody " [0 −9]∗ " $ClassBody1 ∗" " $ClassBody2 ∗" " $ClassBody3 ∗" " $ClassBody1+" " $ClassBody2+" " $ClassBody3+" " $FParams1 + " " $FParams2 + " " $FParam1p " " $FParam1p2 " " $FieldDecl1 +" " $FieldDecl2 +" " $Op " [0 −9]∗ " $Ops0∗ " " $ListOp+" " $ P r e d " [0 −9] " $ P r e d ∗ " [0 −9]∗ " $Sets1 +" " $Sets0∗" " $ S e t " [0 −9]∗ " $ V a r D e c l ∗ " [0 −9]∗ " $ F i e l d D e c l " [0 −9]∗ " $Fields1 ∗" " $ D e c I n t L i t " [0 −9]∗ " $ B B o o l L i t " [0 −9]∗ " $ B S e l I d " [0 −9]∗ −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> −> BlockStatement+ BlockStatement+ BlockStatement+ BlockStatement SwitchBlockStatementGroup SwitchBlockStatementGroup∗ SwitchBlockStatementGroup∗ { I n s t r u c t i o n " ; " }+ { I n s t r u c t i o n " ; " }+ ElseBranch ? ElseBranch ElsifBranch ElsifBranch∗ JElseBranch JElseBranch∗ JElseBranch∗ EitherBranch OrBranch OrBranch ∗ Valuation { V a l u a t i o n " ; " }∗ { V a l u a t i o n " ; " }+ Term Term∗ TermSimple { Term " , " }∗ { Term " , " }∗ { Term " , " }+ { Term " , " }+ ( " ( " ListTermComma " ) " ) ? { TermSimple " , " }+ { TermSimple " , " }+ { TermSimple " , " }+ Condition Primary { P r i m a r y " , " }∗ { P r i m a r y " , " }+ { P r i m a r y " , " }+ Identifier SetInteger Predicate { P r e d i c a t e " ; " }+ ImplementationClause ImplementationClause∗ Header ClauseRefines ( { I d e n t i f i e r " , " }+ ) { I d e n t i f i e r " , " }+ { I d e n t i f i e r " , " }+ ListIdent { I d e n t i f i e r " . " }+ { S e l e c t e d I d e n t " , " }+ ClassBodyDeclaration ClassBodyDeclaration∗ ClassBodyDeclaration∗ ClassBodyDeclaration∗ ClassBodyDeclaration+ ClassBodyDeclaration+ ClassBodyDeclaration+ { F o r m a l P a r a m e t e r " , " }+ { F o r m a l P a r a m e t e r " , " }+ FormalParameter+ FormalParameter+ { F i e l d D e c l a r a t i o n " , " }+ { F i e l d D e c l a r a t i o n " , " }+ OperationB0 { O p e r a t i o n B 0 " ; " }∗ { O p e r a t i o n B 0 " ; " }+ Predicate Predicate+ { S e t " ; " }+ { S e t " ; " }∗ Set { V a r i a b l e D e c l a r a t o r " , " }+ FieldDeclaration+ FieldDeclaration∗ IntegerLiteral BooleanLit SelectedIdent 198 1 2 3 4 5 6 7 8 9 10 11 12 13 B.2 B02Java.asf B.2.1 Regras Globais equations [ main ] s t a r t ( Implementation , $BImpl ) = s t a r t ( C o m p i l a t i o n U n i t , t r d −bcomp ( $BImpl ) ) [ t r d −component ] $ClassBody1∗ : = t r d −c l a u s e s ( $ImpClause ∗0) ===> t r d −bcomp ( IMPLEMENTATION $BId0 $Refines $ImpClause∗0 END) = public class $BId0 extends j a v a x . framework . A p p l e t { $ClassBody1∗ } B.2.2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 Cláusulas [ t r d −0−c l a u s e ] t r d −c l a u s e s ( ) = ; [ t r d −n−c l a u s e s ] $ClassBody1∗ : = t r d −c l a u s e ( $ImpClause1 ) , $ClassBody2∗ : = t r d −c l a u s e s ( $ImpClause ∗ ) ===> t r d −c l a u s e s ( $ImpClause1 $ImpClause ∗ ) = $ClassBody1∗ $ClassBody2∗ [ t r d −n−sees−s e l i d ] $ClassBody1+ : = t r d −sees ( $ L i s t S e l I d +) , $ClassBody2+ : = t r d −sees ( $ S e l I d ) ===> t r d −sees ( $ L i s t S e l I d + , $ S e l I d ) = $ClassBody1+ $ClassBody2+ [ t r d −sees −1] t r d −sees ( $BId0 . $BId1 ) = public class $BId1 { } [ t r d −sees −2] t r d −sees ( $BId0 ) = public class $BId0 { } [ t r d −clause−sees ] $ClassBody1+ : = t r d −sees ( $ L i s t S e l I d +) ===> t r d −c l a u s e (SEES $ L i s t S e l I d +) = $ClassBody1+ [ t r d −n−i m p o r t s ] $ClassBody1+ : = t r d −i m p o r t s ( $Imp1 ) , $ClassBody2+ : = t r d −i m p o r t s ( $Imps1 +) ===> t r d −i m p o r t s ( $Imps1 + , $Imp1 ) = $ClassBody1+ $ClassBody2+ [ t r d −1−import ] t r d −i m p o r t s ( $BId0 . $BId1 ) = { public class $BId1 { } $BId1 $BId0 = new $BId1 ( ) ; } 199 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 [ t r d −clause−i m p o r t s ] $ClassBody1+ : = t r d −i m p o r t s ( $Imps1 +) ===> t r d −c l a u s e (IMPORTS $Imps1 +) = $ClassBody1+ [ t r d −n−promotes ] $ClassBody1+ : = t r d −promotes ( $ L i s t S e l I d +) , $ClassBody2+ : = t r d −promotes ( $ S e l I d ) ===> t r d −promotes ( $ L i s t S e l I d + , $ S e l I d ) = $ClassBody1+ $ClassBody2+ [ t r d −1−promote ] t r d −promotes ( $BId0 . $BId1 ) = public short $BId1 ( short p r ) { short $BId0 = new $BId0 ( p r ) ; r e t u r n $BId0 . $BId1 ( p r ) ; } [ t r d −clause−promotes ] $ClassBody1+ : = t r d −promotes ( $ L i s t S e l I d +) ===> t r d −c l a u s e (PROMOTES $ L i s t S e l I d +) = $ClassBody1+ [ t r d −clause−extends ] t r d −c l a u s e (EXTENDS $ L i s t S e l I d +) = ; [ t r d −sets −n ] $ClassBody1+ : = t r d −s e t s ( $Sets1 +) , $ClassBody2+ : = t r d −s e t s ( $Set1 ) ===> t r d −s e t s ( $Sets1 + ; $Set1 ) = $ClassBody1+ $ClassBody2+ [ t r d −set −1] t r d −s e t s ( $BId0 ) = class $BId0 { } [ t r d −l i d −to−s t a t i c −1] t r d − l i s t i d −to−s t a t i c −const ( $BId0 ) = public s t a t i c f i n a l byte $BId0 ; [ t r d −l i d −to−s t a t i c −n ] $ClassBody1+ : = t r d − l i s t i d −to−s t a t i c −const ( $ L i s t I d +) , $ClassBody2+ : = t r d − l i s t i d −to−s t a t i c −const ( $BId ) ===> t r d − l i s t i d −to−s t a t i c −const ( $ L i s t I d + , $BId ) = $ClassBody1+ $ClassBody2+ [ t r d −set −2] $ClassBody1+ : = t r d − l i s t i d −to−s t a t i c −const ( $ L i s t I d +) ===> t r d −s e t s ( $BId0 = { $ L i s t I d + } ) = public class $BId0 { $ClassBody1+ } [ t r d −clause−s e t s ] $ClassBody1+ : = t r d −s e t s ( $Sets1 +) ===> t r d −c l a u s e (SETS $Sets1+ ) = $ClassBody1+ [ t r d −clause−const ] t r d −c l a u s e (CONSTANTS $ L i s t I d +) = ; 200 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 [ t r d −clause−c c o n s t ] t r d −c l a u s e (CONCRETE_CONSTANTS $ L i s t I d +) = ; [ t r d −clause−prop ] $ClassBody1+ : = t r d −pred−to−const−t y p e ( $Pred ) ===> t r d −c l a u s e (PROPERTIES $Pred ) = $ClassBody1+ [ t r d −clause−v a l u e s ] $ClassBody1+ : = t r d −v a l u a t i o n ( $Vals1 +) ===> t r d −c l a u s e (VALUES $Vals1 +) = $ClassBody1+ [ t r d −v a l u a t i o n −1] $ClassBody1+ : = t r d −v a l u a t i o n ( $Vals1 +) , $ClassBody2+ : = t r d −v a l u a t i o n ( $Val1 ) ===> t r d −v a l u a t i o n ( $Vals1 + ; $Val1 ) = $ClassBody1+ $ClassBody2+ [ t r d −v a l u a t i o n −2] $JPrExpr0 : = t r d −term ( $BTerm0 ) ===> t r d −v a l u a t i o n ( $BId0 = $BTerm0 ) = static { $BId0 = $JPrExpr0 ; } [ t r d −v a l u a t i o n −3] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) ===> t r d −v a l u a t i o n ( $BId0 = BOOL ( $BCond0 ) ) = static { $BId0 = ( ( $JPrExpr0 ) ? t r u e : f a l s e ) ; } [ t r d −clause−v a r ] t r d −c l a u s e (CONCRETE_VARIABLES $ L i s t I d +) = ; [ t r d −clause−i n v ] $ClassBody1+ : = t r d −pred−to−var−t y p e ( $Pred ) ===> t r d −c l a u s e ( INVARIANT $Pred ) = $ClassBody1+ [ t r d −clause−a s s e r t ] t r d −c l a u s e (ASSERTIONS $LPredSemi +) = ; [ t r d −clause− i n i t i a l i s a t i o n ] $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d −c l a u s e ( INITIALISATION $ B I n s t s 1 +) = void INITIALISATION ( ) { $JBlockSt1+ } [ t r d −l i d −param− l i s t −1] t r d − l i s t i d −to−param− l i s t ( $BId1 ) = short $BId1 [ t r d −l i d −param− l i s t −n ] $FParams1+ : = t r d − l i s t i d −to−param− l i s t ( $ListIdComma1 +) , $FParams2+ : = t r d − l i s t i d −to−param− l i s t ( $BId0 ) ===> t r d − l i s t i d −to−param− l i s t ( $ListIdComma1 + , $BId0 ) = $FParams1 + , $FParams2+ [ t r d −clause−op ] $ClassBody1+ : = t r d −o p e r a t i o n s ( $ListOp +) ===> t r d −c l a u s e (OPERATIONS $ListOp +) = $ClassBody1+ 201 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 [ t r d −b−oper−n ] $ClassBody1+ : = t r d −o p e r a t i o n s ( $ListOp +) , $ClassBody2+ : = t r d −o p e r a t i o n s ( $Op1 ) ===> t r d −o p e r a t i o n s ( $ListOp + ; $Op1 ) = $ClassBody1+ $ClassBody2+ [ t r d −b−oper −1] $FParams1+ : = t r d − l i s t i d −to−param− l i s t ( $ListIdComma1 +) , $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t L ) ===> t r d −o p e r a t i o n s ( $BId0 <−− $BId1 ( $ListIdComma1 +) = $ B I n s t L ) = public short $BId1 ( $FParams1 +) { short $BId0 = 0 ; $JBlockSt1+ 247 248 249 250 251 252 253 254 255 256 257 258 259 r e t u r n $BId0 ; } [ t r d −b−oper −2] $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t L ) ===> t r d −o p e r a t i o n s ( $BId0 <−− $BId1 = $ B I n s t L ) = public short $BId1 ( ) { short $BId0 = 0 ; $JBlockSt1+ 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 r e t u r n $BId0 ; } [ t r d −b−oper −3] $FParams1+ : = t r d − l i s t i d −to−param− l i s t ( $ListIdComma1 +) , $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t L ) ===> t r d −o p e r a t i o n s ( $BId1 ( $ListIdComma1 +) = $ B I n s t L ) = public void $BId1 ( $FParams1 +) { $JBlockSt1+ } [ t r d −b−oper −4] $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t L ) ===> t r d −o p e r a t i o n s ( $BId1 = $ B I n s t L ) = public void $BId1 ( ) { $JBlockSt1+ } B.2.3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Instruções [ t r d −i n s t r −n ] $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t 0 ) , $JBlockSt2+ : = t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d −i n s t r u c t i o n s ( $ B I n s t 0 ; $ B I n s t s 1 +) = $JBlockSt1+ $JBlockSt2+ [ t r d −i n s t r −i d e n t i t y ] t r d −i n s t r u c t i o n s ( s k i p ) = ; [ t r d −i n s t r −begin ] $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d −i n s t r u c t i o n s ( BEGIN $ B I n s t s 1 + END) = { $JBlockSt1+ } [ t r d −i n s t r −bcmeq−1] $JPrExpr0 : = t r d −term ( $BTerm0 ) ===> t r d −i n s t r u c t i o n s ( $ S e l I d : = $BTerm0 ) = $ S e l I d = $JPrExpr0 ; 202 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 [ t r d − l i s t −term−comma−1] $JPrExpr1+ : = t r d −term ( $BTerm ) ===> t r d − l i s t −term−comma( $BTerm ) = $JPrExpr1+ [ t r d −l t e r m −comma−n ] $JPrExpr0 : = t r d −term ( $BTerm1 ) , $JPrExpr1+ : = t r d − l i s t −term−comma( $LTermComma1+) ===> t r d − l i s t −term−comma( $BTerm1 , $LTermComma1+) = $JPrExpr0 , $JPrExpr1+ [ t r d −i n s t r −o p c a l l −1] $JPrExpr1+ : = t r d − l i s t −term−comma( $LTermComma1+) ===> t r d −i n s t r u c t i o n s ( $ S e l I d 0 <−− $ S e l I d 1 ( $LTermComma1+) ) = $ S e l I d 0 = $ S e l I d 1 ( $JPrExpr1 +) ; [ t r d −i n s t r −o p c a l l −2] t r d −i n s t r u c t i o n s ( $ S e l I d 0 <−− $ S e l I d 1 ) = $SelId0 = $SelId1 ( ) ; [ t r d −i n s t r −o p c a l l −3] $JPrExpr1+ : = t r d − l i s t −term−comma( $LTermComma1+) ===> t r d −i n s t r u c t i o n s ( $ S e l I d 1 ( $LTermComma1+) ) = $ S e l I d 1 ( $JPrExpr1 +) ; [ t r d −l i d −v a r d e c l −1] t r d − l i s t i d −to−var−d e c l ( $BId1 ) = short $BId1 ; [ t r d −l i d −v a r d e c l −n ] $JBlockSt1+ : = t r d − l i s t i d −to−var−d e c l ( $ListIdComma1 +) , $JBlockSt2+ : = t r d − l i s t i d −to−var−d e c l ( $BId0 ) ===> t r d − l i s t i d −to−var−d e c l ( $ListIdComma1 + , $BId0 ) = $JBlockSt1+ $JBlockSt2+ [ t r d −i n s t r −v a r ] $JBlockSt1+ : = t r d − l i s t i d −to−var−d e c l ( $ListIdComma1 +) , $JBlockSt2+ : = t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d −i n s t r u c t i o n s (VAR $ListIdComma1+ IN $ B I n s t s 1 + END) = $JBlockSt1+ $JBlockSt2+ [ t r d −i n s t r − i f ] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) , $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d −i n s t r u c t i o n s ( I F $BCond0 THEN $ B I n s t s 1 + END) = i f ( $JPrExpr0 ) { $JBlockSt1+ } [ t r d −i n s t r −i f −else ] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) , $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) , $JBlockSt2 ∗ : = t r d −else ( $Else ? ) ===> t r d −i n s t r u c t i o n s ( I F $BCond0 THEN $ B I n s t s 1 + $Else ? END) = i f ( $JPrExpr0 ) { $JBlockSt1+ } else { $JBlockSt2 ∗ } 203 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 [ t r d −i n s t r −i f − e l s i f ] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) , $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) , $JBlockSt1 ∗ : = t r d −e l s i f s ( $ E l s i f 0 ∗ ) ===> t r d −i n s t r u c t i o n s ( I F $BCond0 THEN $ B I n s t s 1 + $Elsif0∗ END) = i f ( $JPrExpr0 ) { $JBlockSt1+ } else { $JBlockSt1 ∗ } [ t r d −i n s t r −i f −e l s i f −else ] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) , $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) , $JBlockSt1 ∗ : = t r d −e l s i f s ( $ E l s i f 0 ∗ ) , $JBlockSt2 ∗ : = t r d −else ( $Else ? ) ===> t r d −i n s t r u c t i o n s ( I F $BCond0 THEN $ B I n s t s 1 + $Elsif0∗ $Else ? END) = i f ( $JPrExpr0 ) { $JBlockSt1+ } else { $JBlockSt1 ∗ $JBlockSt2 ∗ } [ t r d −else−b r ] $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d −else ( ELSE $ B I n s t s 1 +) = $JBlockSt1+ [ t r d −e l s i f −b r ] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) , $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) , $JBlockSt1 ∗ : = t r d −e l s i f s ( $ E l s i f 0 ∗ ) ===> t r d −e l s i f s ( ELSIF $BCond0 THEN $ B I n s t s 1 + $ E l s i f 0 ∗ ) = i f ( $JPrExpr0 ) { $JBlockSt1+ } else { $JBlockSt1 ∗ } [ t r d −e l s i f s −br −0] t r d −e l s i f s ( ) = [ t r d −e l s i f s −br−n ] $JBlockSt1 ∗ : = t r d − e l s i f ( $ E l s i f 1 ) , $JBlockSt2 ∗ : = t r d −e l s i f s ( $ E l s i f 0 ∗ ) ===> t r d −e l s i f s ( $ E l s i f 1 $ E l s i f 0 ∗ ) = $JBlockSt1 ∗ $JBlockSt2 ∗ [ t r d −i n s t r −case−else ] $JPrExpr0 : = t r d −term ( $BTermSimp0 ) , $JPrExpr1 : = t r d −term ( $BTermSimp1 ) , $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) , $JSwitchBlock0 ∗ : = t r d −o r s ( $Or0 ∗ ) , $JBlockSt2+ : = t r d −else ( $Else ? ) ===> t r d −i n s t r u c t i o n s ( CASE $BTermSimp0 OF EITHER $BTermSimp1 THEN $ B I n s t s 1 + 204 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 $Or0∗ $Else ? END END) = switch ( $JPrExpr0 ) { case $JPrExpr1 : { $JBlockSt1+ break ; } $JSwitchBlock0 ∗ default : { $JBlockSt2+ break ; } } [ t r d −i n s t r −case−noelse ] $JPrExpr0 : = t r d −term ( $BTermSimp0 ) , $JPrExpr1 : = t r d −term ( $BTermSimp1 ) , $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) , $JSwitchBlock0 ∗ : = t r d −o r s ( $Or0 ∗ ) ===> t r d −i n s t r u c t i o n s ( CASE $BTermSimp0 OF EITHER $BTermSimp1 THEN $ B I n s t s 1 + $Or0∗ END END) = switch ( $JPrExpr0 ) { case $JPrExpr1 : { $JBlockSt1+ break ; } $JSwitchBlock0 ∗ } [ t r d −case−or −0] t r d −o r s ( ) = [ t r d −case−or−n ] $JSwitchBlock0 ∗ : = t r d −o r ( $Or ) , $JSwitchBlock1 ∗ : = t r d −o r s ( $Or0 ∗ ) ===> t r d −o r s ( $Or $Or0 ∗ ) = $JSwitchBlock0 ∗ $JSwitchBlock1 ∗ [ t r d −case−o r ] $JPrExpr0 : = t r d −term ( $BTermSimp0 ) , $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d −o r (OR $BTermSimp0 THEN $ B I n s t s 1 +) = case $JPrExpr0 : { $JBlockSt1+ break ; } [ t r d −i n s t r −while ] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) , $JBlockSt1+ : = t r d −i n s t r u c t i o n s ( $ B I n s t s 1 +) ===> t r d −i n s t r u c t i o n s ( WHILE $BCond0 DO $BInsts1+ INVARIANT $BCond1 VARIANT $BTerm0 END) = while ( $JPrExpr0 ) { $JBlockSt1+ } 205 B.2.4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 Termos [ t r d −bterm−s e l i d −1] t r d −term ( $ S e l I d ) = $ S e l I d [ t r d −bterm−bool −1] t r d −term (TRUE) = t r u e [ t r d −bterm−bool −2] t r d −term ( FALSE ) = f a l s e [ t r d −bterm−i n t r −1] t r d −term ( $ D e c I n t L i t ) = $ D e c I n t L i t [ t r d −bterm−i n t r −2] t r d −term ( MAXINT ) = I n t e g e r . MAX_VALUE [ t r d −bterm−i n t r −3] t r d −term ( MININT ) = I n t e g e r . MIN_VALUE [ t r d −bterm−set−empty ] t r d −term ( { } ) = n u l l [ t r d −bterm−seq−empty ] t r d −term ( [ ] ) = n u l l [ t r d −bterm−paren ] $JPrExpr0 : = t r d −term ( $BTerm0 ) ===> t r d −term ( ( $BTerm0 ) ) = ( $JPrExpr0 ) [ t r d −bterm−op−res −1] $JPrExpr : = t r d −c o n d i t i o n ( $BCond0 ) ===> t r d −term ( b o o l ( $BCond0 ) ) = ( $JPrExpr ) ? t r u e : f a l s e [ t r d −bterm−op−res −2] $JPrExpr0 : = t r d −term ( $BTerm0 ) ===> t r d −term ( succ ( $BTerm0 ) ) = ( ( $JPrExpr0 ) + 1 ) [ t r d −bterm−op−res −3] $JPrExpr0 : = t r d −term ( $BTerm0 ) ===> t r d −term ( pred ( $BTerm0 ) ) = ( ( $JPrExpr0 ) − 1 ) [ t r d −bterm−exp−ar −1] $JPrExpr0 : = t r d −term ( $BTerm0 ) , $JPrExpr1 : = t r d −term ( $BTerm1 ) ===> t r d −term ( $BTerm0 + $BTerm1 ) = $JPrExpr0 + $JPrExpr1 [ t r d −bterm−exp−ar −2] $JPrExpr0 : = t r d −term ( $BTerm0 ) , $JPrExpr1 : = t r d −term ( $BTerm1 ) ===> t r d −term ( $BTerm0 − $BTerm1 ) = $JPrExpr0 − $JPrExpr1 [ t r d −bterm−exp−ar −3] $JPrExpr0 : = t r d −term ( $BTerm0 ) ===> t r d −term (−$BTerm0 ) = −($JPrExpr0 ) [ t r d −bterm−exp−ar −4] $JPrExpr0 : = t r d −term ( $BTerm0 ) , $JPrExpr1 : = t r d −term ( $BTerm1 ) ===> t r d −term ( $BTerm0 ∗ $BTerm1 ) = $JPrExpr0 ∗ $JPrExpr1 206 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 [ t r d −bterm−exp−ar −5] $JPrExpr0 : = t r d −term ( $BTerm0 ) , $JPrExpr1 : = t r d −term ( $BTerm1 ) ===> t r d −term ( $BTerm0 / $BTerm1 ) = $JPrExpr0 / $JPrExpr1 [ t r d −bterm−exp−ar −6] $JPrExpr0 : = t r d −term ( $BTerm0 ) , $JPrExpr1 : = t r d −term ( $BTerm1 ) ===> t r d −term ( $BTerm0 mod $BTerm1 ) = $JPrExpr0 % $JPrExpr1 [ t r d −bterm−exp−ar −7] $JPrExpr0 : = t r d −term ( $BTerm0 ) , $JPrExpr1 : = t r d −term ( $BTerm1 ) ===> t r d −term ( $BTerm0 ∗∗ $BTerm1 ) = Math . pow ( $JPrExpr0 , $JPrExpr1 ) [ t r d −bterm−i n t e r v a l b 0 ] $JPrExpr0 : = t r d −term ( $BTerm0 ) , $JPrExpr1 : = t r d −term ( $BTerm1 ) ===> t r d −term ( $BTerm0 . . $BTerm1 ) = new B 0 I n t e r v a l ( $JPrExpr0 , $JPrExpr1 ) B.2.5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 Condições [ t r d −cond −1] $JPrExpr0 : = t r d −term ( $BTermSimp0 ) , $JPrExpr1 : = t r d −term ( $BTermSimp1 ) ===> t r d −c o n d i t i o n ( $BTermSimp0 = $BTermSimp1 ) = $JPrExpr0 == $JPrExpr1 [ t r d −cond −2] $JPrExpr0 : = t r d −term ( $BTermSimp0 ) , $JPrExpr1 : = t r d −term ( $BTermSimp1 ) ===> t r d −c o n d i t i o n ( $BTermSimp0 / = $BTermSimp1 ) = $JPrExpr0 ! = $JPrExpr1 [ t r d −cond −3] $JPrExpr0 : = t r d −term ( $BTermSimp0 ) , $JPrExpr1 : = t r d −term ( $BTermSimp1 ) ===> t r d −c o n d i t i o n ( $BTermSimp0 < $BTermSimp1 ) = $JPrExpr0 < $JPrExpr1 [ t r d −cond −4] $JPrExpr0 : = t r d −term ( $BTermSimp0 ) , $JPrExpr1 : = t r d −term ( $BTermSimp1 ) ===> t r d −c o n d i t i o n ( $BTermSimp0 > $BTermSimp1 ) = $JPrExpr0 > $JPrExpr1 [ t r d −cond −5] $JPrExpr0 : = t r d −term ( $BTermSimp0 ) , $JPrExpr1 : = t r d −term ( $BTermSimp1 ) ===> t r d −c o n d i t i o n ( $BTermSimp0 <= $BTermSimp1 ) = $JPrExpr0 <= $JPrExpr1 [ t r d −cond −6] $JPrExpr0 : = t r d −term ( $BTermSimp0 ) , $JPrExpr1 : = t r d −term ( $BTermSimp1 ) ===> t r d −c o n d i t i o n ( $BTermSimp0 >= $BTermSimp1 ) = $JPrExpr0 >= $JPrExpr1 [ t r d −cond −7] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) , $JPrExpr1 : = t r d −c o n d i t i o n ( $BCond1 ) ===> t r d −c o n d i t i o n ( $BCond0 & $BCond1 ) = $JPrExpr0 && $JPrExpr1 207 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 [ t r d −cond −8] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) , $JPrExpr1 : = t r d −c o n d i t i o n ( $BCond1 ) ===> t r d −c o n d i t i o n ( $BCond0 o r $BCond1 ) = $JPrExpr0 | | $JPrExpr1 [ t r d −cond −9] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) ===> t r d −c o n d i t i o n ( n o t ( $BCond0 ) ) = ! ( $JPrExpr0 ) [ t r d −cond −10] $JPrExpr0 : = t r d −c o n d i t i o n ( $BCond0 ) ===> t r d −c o n d i t i o n ( ( $BCond0 ) ) = ( $JPrExpr0 ) B.2.6 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 Predicados [ t r d −pred−var−tp−paren ] $ClassBody1∗ : = t r d −pred−to−var−t y p e ( $Pred1 ) ===> t r d −pred−to−var−t y p e ( ( $Pred1 ) ) = $ClassBody1∗ [ t r d −pred−var−tp−and ] $ClassBody1∗ : = t r d −pred−to−var−t y p e ( $Pred1 ) , $ClassBody2∗ : = t r d −pred−to−var−t y p e ( $Pred2 ) ===> t r d −pred−to−var−t y p e ( $Pred1 & $Pred2 ) = $ClassBody1∗ $ClassBody2∗ [ t r d −pred−tp−i n t ] t r d −pred−to−var−t y p e ( $BId0 : INT ) = i n t $BId0 ; [ t r d −pred−tp−n a t ] t r d −pred−to−var−t y p e ( $BId0 : NAT) = i n t $BId0 ; [ t r d −pred−var−tp−short ] $BId1 == JSHORT ===> t r d −pred−to−var−t y p e ( $BId0 : $BId1 ) = short $BId0 ; [ t r d −pred−var−tp−byte ] $BId1 == JBYTE ===> t r d −pred−to−var−t y p e ( $BId0 : $BId1 ) = byte $BId0 ; [ t r d −pred−var−tp−bool −1] $BId1 == JBOOLEAN ===> t r d −pred−to−var−t y p e ( $BId0 : $BId1 ) = boolean $BId0 ; [ t r d −pred−var−tp−bool −2] t r d −pred−to−var−t y p e ( $BId0 : BOOL) = boolean $BId0 ; [ t r d −pred−var−tp−o b j e c t ] t r d −pred−to−var−t y p e ( $BId0 : $BId1 ) = $BId1 $BId0 ; [ t r d −pred−var−tp−a r r a y −byte ] $BId2 == JBYTE ===> t r d −pred−to−var−t y p e ( $BId0 : $BId1 −−> $BId2 ) = byte [ ] $BId0 ; [ t r d −pred−var−tp−a r r a y −short ] $BId2 == JSHORT ===> t r d −pred−to−var−t y p e ( $BId0 : $BId1 −−> $BId2 ) = public short [ ] $BId0 ; [ t r d −pred−var−tp−a r r a y −boolean ] $BId2 == JBOOLEAN ===> 208 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 t r d −pred−to−var−t y p e ( $BId0 : $BId1 −−> $BId2 ) = public boolean [ ] $BId0 ; [ t r d −pred−var−tp−seq−byte ] t r d −pred−to−var−t y p e ( $BId0 : seq ( JBYTE ) ) = public byte [ ] $BId0 ; [ t r d −pred−var−tp−seq−short ] t r d −pred−to−var−t y p e ( $BId0 : seq (JSHORT) ) = public short [ ] $BId0 ; [ t r d −pred−var−tp−seq−boolean ] t r d −pred−to−var−t y p e ( $BId0 : seq (JBOOLEAN) ) = public boolean [ ] $BId0 ; [ t r d −pred−var−tp−seq1−byte ] t r d −pred−to−var−t y p e ( $BId0 : seq1 ( JBYTE ) ) = public byte [ ] $BId0 ; [ t r d −pred−var−tp−seq1−short ] t r d −pred−to−var−t y p e ( $BId0 : seq1 (JSHORT) ) = public short [ ] $BId0 ; [ t r d −pred−var−tp−seq1−boolean ] t r d −pred−to−var−t y p e ( $BId0 : seq1 (JBOOLEAN) ) = public boolean [ ] $BId0 ; [ t r d −pred−const−tp−and ] $ClassBody1∗ : = t r d −pred−to−const−t y p e ( $Pred1 ) , $ClassBody2∗ : = t r d −pred−to−const−t y p e ( $Pred2 ) ===> t r d −pred−to−const−t y p e ( $Pred1 & $Pred2 ) = $ClassBody1∗ $ClassBody2∗ [ t r d −pred−const−tp−paren ] $ClassBody1∗ : = t r d −pred−to−const−t y p e ( $Pred1 ) ===> t r d −pred−to−const−t y p e ( ( $Pred1 ) ) = $ClassBody1∗ [ t r d −pred−const−tp−i n t ] t r d −pred−to−const−t y p e ( $BId0 : INT ) = public s t a t i c f i n a l i n t $BId0 ; [ t r d −pred−const−tp−n a t ] t r d −pred−to−const−t y p e ( $BId0 : NAT) = public s t a t i c f i n a l i n t $BId0 ; [ t r d −pred−const−tp−short ] $BId1 == JSHORT ===> t r d −pred−to−const−t y p e ( $BId0 : $BId1 ) = public s t a t i c f i n a l short $BId0 ; [ t r d −pred−const−tp−byte ] $BId1 == JBYTE ===> t r d −pred−to−const−t y p e ( $BId0 : $BId1 ) = public s t a t i c f i n a l byte $BId0 ; [ t r d −pred−const−tp−bool −1] t r d −pred−to−const−t y p e ( $BId0 : BOOL) = public s t a t i c f i n a l boolean $BId0 ; [ t r d −pred−const−tp−bool −2] $BId1 == JBOOLEAN ===> t r d −pred−to−const−t y p e ( $BId0 : $BId1 ) = public s t a t i c f i n a l boolean $BId0 ; [ t r d −pred−const−tp−o b j e c t ] t r d −pred−to−const−t y p e ( $BId0 : $BId1 ) = public s t a t i c f i n a l $BId1 $BId0 ; [ t r d −pred−const−tp−a r r a y −byte ] $BId2 == JBYTE ===> t r d −pred−to−const−t y p e ( $BId0 : $BId1 −−> $BId2 ) = public s t a t i c f i n a l byte [ ] $BId0 ; [ t r d −pred−const−tp−a r r a y −short ] $BId2 == JSHORT ===> t r d −pred−to−const−t y p e ( $BId0 : $BId1 −−> $BId2 ) = public s t a t i c f i n a l short [ ] $BId0 ; 209 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 [ t r d −pred−const−tp−a r r a y −boolean ] $BId2 == JBOOLEAN ===> t r d −pred−to−const−t y p e ( $BId0 : $BId1 −−> $BId2 ) = public s t a t i c f i n a l boolean [ ] $BId0 ; [ t r d −pred−const−tp−seq−byte ] t r d −pred−to−const−t y p e ( $BId0 : seq ( JBYTE ) ) = public s t a t i c f i n a l byte [ ] $BId0 ; [ t r d −pred−const−tp−seq−short ] t r d −pred−to−const−t y p e ( $BId0 : seq (JSHORT) ) = public s t a t i c f i n a l short [ ] $BId0 ; [ t r d −pred−const−tp−seq−boolean ] t r d −pred−to−const−t y p e ( $BId0 : seq (JBOOLEAN) ) = public s t a t i c f i n a l boolean [ ] $BId0 ; [ t r d −pred−const−tp−seq1−byte ] t r d −pred−to−const−t y p e ( $BId0 : seq1 ( JBYTE ) ) = public s t a t i c f i n a l byte [ ] $BId0 ; [ t r d −pred−const−tp−seq1−short ] t r d −pred−to−const−t y p e ( $BId0 : seq1 (JSHORT) ) = public s t a t i c f i n a l short [ ] $BId0 ; [ t r d −pred−const−tp−seq1−boolean ] t r d −pred−to−const−t y p e ( $BId0 : seq1 (JBOOLEAN) ) = public s t a t i c f i n a l boolean [ ] $BId0 ; 210 APÊNDICE C -- Especificação do Passaporte Eletrônico C.1 Máquina Abstrata Passport MACHINE Passport SEES JByte, JShort, JBoolean, Passport_Context, FileSystem_Context INCLUDES fileSystem.FileSystem VARIABLES /* persistent state */ perState, /* volatile state */ volState, /* file that is current selected */ selectedFile, /* a generated random number (for BAC) */ rnd, /* sequence number for secure messaging */ ssc INVARIANT perState ∈ JBYTE ∧ volState ∈ seq1(JBYTE) ∧ size (volState) = 1 ∧ selectedFile ∈ EF_DG1_INDEX .. SOS_LOG_INDEX ∧ rnd ∈ seq1 (JBYTE) ∧ ssc ∈ seq1 (JBYTE) INITIALISATION perState := PER_STATE_INIT || volState := [NO_FILE_SELECTED] || selectedFile := EF_DG1_INDEX || rnd := [0, 0, 0, 0, 0, 0, 0, 0] || ssc := [0, 0, 0, 0, 0, 0, 0, 0] OPERATIONS /*Processes the first part of a request for a Passport stored file. It stores the ’id’ of the requested file in selectedFile state variable. After, The client must call readBinary service to receive the file. APDU = SELECT_FILE @param fid - the id of the file to select. */ processSelectFile (fid)= PRE fid ∈ EF_DG1_INDEX .. SOS_LOG_INDEX ∧ volState(1) = MUTUAL_AUTHENTICATED ∧ ¬ (perState = LOCKED) THEN CHOICE selectedFile := fid || volState(1) := FILE_SELECTED OR volState(1) := NO_FILE_SELECTED END END; 211 /** Reads the current selected file and sends it back to the client. * APDU = READ_BINARY * @param le - the expected response length. * @return file - the requested file */ file ← processReadBinary (protectedAPDU, le)= PRE protectedAPDU ∈ JBOOLEAN ∧ le ∈ JSHORT ∧ volState(1) = FILE_SELECTED THEN file ← fileSystem.getFile(selectedFile) END; /** Generates random sequence and stores it in ’rnd’ state variable. This random number is required for BAC and EAC protocols. * APDU = GET_CHALENGE * @param protectedAPDU - if secure messaging is used * @param le - the expected length of response * @return rnd_val - the generated random number */ rnd_val ← processGetChallenge (protectedAPDU, le) = PRE protectedAPDU ∈ JBOOLEAN ∧ le ∈ JSHORT ∧ perState = HAS_MUTUAL_AUTHENTICATION_KEYS ∧ ¬ (volState(1) = MUTUAL_AUTHENTICATED) THEN volState(1) := CHALLENGED || ANY number WHERE number = seq1(JBYTE) THEN rnd :∈ number || rnd_val :∈ number END END; /** Processes authentication part of BAC and EAC protocols. * @param protectedAPDU - if secure messaging is used * @return key_data - the mutual authentication key */ key_data ← processMutualAuthenticate (protectedAPDU)= PRE protectedAPDU ∈ JBOOLEAN ∧ (volState(1) = CHALLENGED ∧ ¬ (volState(1) = MUTUAL_AUTHENTICATED)) THEN volState(1) := MUTUAL_AUTHENTICATED || ssc :∈ seq1(JBYTE) || key_data :∈ seq1(JBYTE) END; res ← getPersistentState = BEGIN res := perState END; res ← getVolatileState = BEGIN res := volState END END 212 C.2 Máquina Abstrata Java Card - Passport_JC MACHINE Passport_JC SEES JByte, JShort,JBoolean, ISO7816, Apdu_Properties, Passport_Context, FileSystem_Context INCLUDES fileSystem.FileSystem, ISOException.ISOException ABSTRACT_VARIABLES jc_perState, jc_volState, jc_selectedFile, jc_rnd, jc_ssc INVARIANT jc_perState ∈ JBYTE ∧ jc_volState ∈ seq1(JBYTE) ∧ size (jc_volState) = 1 ∧ jc_selectedFile ∈ EF_DG1_INDEX .. SOS_LOG_INDEX ∧ jc_rnd ∈ seq1 (JBYTE) ∧ jc_ssc ∈ seq1 (JBYTE) INITIALISATION jc_perState := PER_STATE_INIT || jc_volState := [NO_FILE_SELECTED] || jc_selectedFile := EF_DG1_INDEX || jc_rnd := [0, 0, 0, 0, 0, 0, 0, 0] || jc_ssc := [0, 0, 0, 0, 0, 0, 0, 0] OPERATIONS processSelectFile (apdu_p, data_in) = PRE apdu_p ∈ APDU ∧ data_in ∈ seq1(JBYTE) ∧ data_in(1) ∈ EF_DG1_INDEX .. SOS_LOG_INDEX ∧ jc_volState(1) = MUTUAL_AUTHENTICATED ∧ ¬ (jc_perState = LOCKED) THEN CHOICE jc_selectedFile := data_in(1) || jc_volState(1) := FILE_SELECTED OR jc_volState(1) := NO_FILE_SELECTED END END; data_out ← processReadBinary (apdu_p, protectedAPDU, data_in) = PRE apdu_p ∈ APDU ∧ protectedAPDU ∈ JBOOLEAN ∧ data_in ∈ seq1(JBYTE) ∧ jc_volState(1) = FILE_SELECTED THEN data_out ← fileSystem.getFile (jc_selectedFile) END; 213 data_out ← processGetChallenge (apdu_p, protectedAPDU, data_in) = PRE apdu_p ∈ APDU ∧ protectedAPDU ∈ JBOOLEAN ∧ data_in ∈ seq1(JBYTE) ∧ jc_perState = HAS_MUTUAL_AUTHENTICATION_KEYS ∧ ¬ (jc_volState(1) = MUTUAL_AUTHENTICATED) THEN jc_volState(1) := CHALLENGED || ANY number WHERE number = seq1(JBYTE) THEN jc_rnd :∈ number || data_out :∈ number END END; data_out ← processMutualAuthenticate (apdu_p, protectedAPDU) = PRE apdu_p ∈ APDU ∧ protectedAPDU ∈ JBOOLEAN ∧ (jc_volState(1) = CHALLENGED ∧ ¬ (jc_volState(1) = MUTUAL_AUTHENTICATED)) THEN jc_volState(1) := MUTUAL_AUTHENTICATED || jc_ssc :∈ seq1(JBYTE) || data_out :∈ seq1(JBYTE) END; res ← getPersistentState = BEGIN res := jc_perState END; res ← getVolatileState = BEGIN res := jc_volState(1) END END 214 C.3 Refinamento Full Function - Passport_JC_FF_ref REFINEMENT Passport_JC_FF_ref REFINES Passport_JC SEES JByte, JShort, JBoolean, ISO7816, Apdu_Properties, Passport_Context, FileSystem_Context, Passport_JC_FF_Context INCLUDES fileSystem.FileSystem, ISOException.ISOException ABSTRACT_VARIABLES jc_perState, jc_volState, jc_selectedFile, jc_rnd, jc_ssc INVARIANT jc_perState ∈ JBYTE ∧ jc_volState ∈ seq1(JBYTE) ∧ size (jc_volState) = 1 ∧ jc_selectedFile ∈ EF_DG1_INDEX .. SOS_LOG_INDEX ∧ jc_rnd ∈ seq1(JBYTE) ∧ jc_ssc ∈ seq1(JBYTE) INITIALISATION jc_perState := PER_STATE_INIT || jc_volState := [NO_FILE_SELECTED] || jc_selectedFile := EF_DG1_INDEX || jc_rnd := [0, 0, 0, 0, 0, 0, 0, 0] || jc_ssc := [0, 0, 0, 0, 0, 0, 0, 0] OPERATIONS processSelectFile (apdu_p, data_in) = IF jc_perState = LOCKED THEN ISOException.throwIt(ex_processSelectFile0) ELSIF ¬ (jc_volState(1) = MUTUAL_AUTHENTICATED) THEN ISOException.throwIt(ex_processSelectFile1) ELSE CHOICE jc_selectedFile := data_in(1) || jc_volState(1) := FILE_SELECTED OR jc_volState(1) := NO_FILE_SELECTED END END; data_out ← processReadBinary (apdu_p, protectedAPDU, data_in) = IF ¬ (jc_volState(1) = FILE_SELECTED) THEN ISOException.throwIt(ex_processReadBinary0) ELSE data_out ← fileSystem.getFile (jc_selectedFile) END; data_out ← processGetChallenge (apdu_p, protectedAPDU, data_in) = IF jc_volState(1) = MUTUAL_AUTHENTICATED THEN ISOException.throwIt(ex_processGetChallenge0) ELSIF ¬ (jc_perState = HAS_MUTUAL_AUTHENTICATION_KEYS) THEN ISOException.throwIt(ex_processGetChallenge1) ELSE jc_volState(1) := CHALLENGED || ANY number WHERE number = seq1(JBYTE) THEN jc_rnd :∈ number || data_out :∈ number END END; 215 data_out ← processMutualAuthenticate (apdu_p, protectedAPDU) = IF ¬ (jc_volState(1) = CHALLENGED ∧ ¬ (jc_volState(1) = MUTUAL_AUTHENTICATED)) THEN ISOException.throwIt(ex_processMutualAuthenticate0) ELSE jc_volState(1) := MUTUAL_AUTHENTICATED || jc_ssc :∈ seq1(JBYTE) || data_out :∈ seq1(JBYTE) END; res ← getPersistentState = BEGIN res := jc_perState END; res ← getVolatileState = BEGIN res := jc_volState(1) END END 216 C.4 Implementação port_JC_imp Principal IMPLEMENTATION Passport_JC_imp REFINES Passport_JC_FF_ref SEES JByte, JShort,JBoolean, Apdu_Properties, ISO7816, Passport_Context, FileSystem_Context, Passport_JC_FF_Context IMPORTS per_state.LM_BYTE, vol_state.LM_SEQ_0, sel_file.LM_SHORT_FILE, jc_rnd_var.LM_SEQ_1, jc_ssc_var.LM_SEQ_1, fileSystem.FileSystem, passportUtil.PassportUtil, ISOException.ISOException, randomData.RandomData, crypto.PassportCrypto, keyStore.KeyStore, apdu.ApduUtil.Util INVARIANT jc_perState = per_state.num_byte ∧ jc_volState = vol_state.bt_seq ∧ jc_selectedFile = sel_file.num_short ∧ jc_rnd = jc_rnd_var.bt_seq ∧ jc_ssc = jc_ssc_var.bt_seq OPERATIONS processSelectFile (apdu_p, data_in) = VAR v_st, per_st, bf, lc_data, fid, file IN v_st ← vol_state.getFirst; per_st ← per_state.get_num; bf ← apdu.getBuffer; vol_state.set(NO_FILE_SELECTED); IF per_st = LOCKED THEN ISOException.throwIt(ex_processSelectFile0) ELSIF ¬ (v_st = MUTUAL_AUTHENTICATED) THEN ISOException.throwIt(ex_processSelectFile1) ELSE fid := data_in(1); IF (fid ≥ EF_DG1_INDEX ∧ fid ≤ SOS_LOG_INDEX) THEN file ← fileSystem.getFile(fid); IF (file 6= []) THEN sel_file.set_num(fid); vol_state.set(FILE_SELECTED) ELSE ISOException.throwIt(SW_FILE_NOT_FOUND) END ELSE ISOException.throwIt(SW_INTERNAL_ERROR) END END END; dos Serviços - Pass- 217 C.4.1 Passport_JC_imp - Operação processReadBinary data_out ← processReadBinary (apdu_p, protectedAPDU, data_in) = VAR v_st, sel_file, bf, p1, p2, len, offset, le IN v_st ← vol_state.getFirst; sel_file ← sel_file.get_num; len := cast_short (0); bf ← apdu.getBuffer; le := cast_short (0); IF ¬ (v_st = FILE_SELECTED) THEN ISOException.throwIt(ex_processReadBinary0) ELSE p1 := cast_byte(0); p2 := cast_byte(0); IF size (bf) ≥ 4 ∧ bf(OFFSET_LC) ≤ MINBYTE ∧ bf(OFFSET_LC) ≥ MAXBYTE THEN p1 := bf(OFFSET_P1); p2 := bf(OFFSET_P2) ELSE offset := cast_short (0); data_out ← fileSystem.getFile (sel_file); IF (data_out = []) THEN ISOException.throwIt (SW_FILE_NOT_FOUND) ELSE VAR file_size, size_bf,bf_offset, out IN size_bf := cast_short (size(bf)); IF sum_short(cast_short(0), 2) ≤ size(data_in) THEN le ← Util.getShort(data_in, cast_short (0)) END; file_size ← fileSystem.getFileSize(sel_file); IF (size_bf ≥ 0 ∧ offset ≥ 0 ∧ subt_short(file_size, offset) ≥ 0 ∧ subt_short(size_bf, 37) ≥ 0 ∧ le ≥ 0) THEN len ← passportUtil.minimum(subt_short (size_bf, 37), subt_short (file_size, offset)); len ← passportUtil.minimum(len, size_bf); len ← passportUtil.minimum(le, len); bf_offset := 0; IF (protectedAPDU = TRUE) THEN bf_offset ← crypto.getAPDUBufferOffset(len) ELSE IF sum_short(cast_short(0), len) ≤ size(data_out) ∧ sum_short(offset, len) ≤ size(data_out) ∧ bf_offset ≥ 0 ∧ bf 6= [] ∧ sum_short(bf_offset, len) ≤ size(bf) THEN out ← Util.arrayCopyNonAtomic ( data_out, offset, bf, bf_offset, len) END END END END END END END END; 218 C.4.2 Passport_JC_imp - Operação processGetChallenge data_out ← processGetChallenge (apdu_p, protectedAPDU, data_in) = VAR le_val, v_st, per_st,bf, bf_offset, jc_rnd, apdu_state, out IN bf ← apdu.getBuffer; v_st ← vol_state.getFirst; per_st ← per_state.get_num; jc_rnd ← jc_rnd_var.getSeq; IF v_st = MUTUAL_AUTHENTICATED THEN ISOException.throwIt(ex_processGetChallenge0) ELSIF ¬ (per_st = HAS_MUTUAL_AUTHENTICATION_KEYS) THEN ISOException.throwIt(ex_processGetChallenge1) ELSE apdu_state ← apdu.getCurrentState; le_val := cast_short (0); IF size (data_in) ≥ 2 THEN le_val ← Util.getShort(data_in, cast_short (0)) ELSIF (protectedAPDU = FALSE ∧ apdu_state = STATE_INITIAL) THEN le_val ← apdu.setOutgoing ELSE IF (le_val 6= 8) THEN ISOException.throwIt(SW_WRONG_LENGTH) ELSE randomData.generateData(jc_rnd, 0, le_val); IF (protectedAPDU = TRUE) THEN bf_offset ← crypto.getAPDUBufferOffset(le_val) ELSE bf_offset := cast_short(0); IF sum_short(cast_short(0), le_val) ≤ size(jc_rnd) ∧ sum_short(cast_short(0), le_val) ≤ size(bf) ∧ bf_offset ≥ 0 ∧ bf 6= [] ∧ sum_short(bf_offset, le_val) ≤ size(bf) THEN out ← Util.arrayCopyNonAtomic( jc_rnd, cast_short(0), bf, bf_offset, le_val); jc_rnd_var.setSeq (jc_rnd) ELSE ISOException.throwIt(SW_INTERNAL_ERROR) END END END END; vol_state.set(CHALLENGED); data_out := jc_rnd END END; 219 C.4.3 Passport_JC_imp - Operação processMutualAuthenticate data_out ← processMutualAuthenticate (apdu_p, protectedAPDU) = VAR bf,apdu_state, bytesLeft, e_ifd_p, e_ifd_length, m_ifd_p, out, res, plaintex_len, v_st,jc_rnd_ifd_p, jc_rnd_icc_p, k_ifd_p, k_icc_p,keySeed_p, keys_p, ciphertext_len, jc_rnd_val, out_array, encKey_p, macKey_p, res_cp, jc_ssc IN bf ← apdu.getBuffer; v_st ← vol_state.getFirst; ciphertext_len := cast_short(1); IF ¬ (v_st = CHALLENGED ∧ ¬ (v_st = MUTUAL_AUTHENTICATED)) THEN ISOException.throwIt (SW_SECURITY_STATUS_NOT_SATISFIED) ELSE bytesLeft := cast_byte (0); IF size (bf) ≥ 4 ∧ bf(OFFSET_LC) ≤ MINBYTE ∧ bf(OFFSET_LC) ≥ MAXBYTE THEN bytesLeft := bf(OFFSET_LC) ELSE e_ifd_length := sum_short (mult_short (RND_LENGTH, 2), KEYMATERIAL_LENGTH); IF (bytesLeft 6= sum_short (e_ifd_length, MAC_LENGTH)) THEN ISOException.throwIt(SW_WRONG_LENGTH) ELSE e_ifd_p := OFFSET_CDATA; m_ifd_p := sum_short (e_ifd_p, e_ifd_length); apdu_state ← apdu.getCurrentState; IF (apdu_state = STATE_INITIAL) THEN out ← apdu.setIncomingAndReceive ELSIF (apdu_state 6= STATE_FULL_INCOMING) THEN ISOException.throwIt(SW_INTERNAL_ERROR); IF (e_ifd_length ≥ 0 ∧ e_ifd_p ≥ 0 ∧ m_ifd_p ≥ 0 ∧ bf 6= []) THEN crypto.initMac(MODE_VERIFY); res := FALSE; plaintex_len := cast_short (0); res ← crypto.verifyMacFinal( bf, e_ifd_p, e_ifd_length, bf, m_ifd_p ); IF (res = FALSE) THEN ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED) ELSE crypto.decryptInit; plaintex_len ← crypto.decryptFinal ( bf, e_ifd_p, e_ifd_length, bf, cast_short(0)); IF (plaintex_len 6= e_ifd_length) THEN ISOException.throwIt(SW_INTERNAL_ERROR) ELSE jc_rnd_ifd_p := 0; jc_rnd_icc_p := RND_LENGTH; k_ifd_p := sum_short (jc_rnd_icc_p , RND_LENGTH); k_icc_p := sum_short (k_ifd_p, KEYMATERIAL_LENGTH); keySeed_p := sum_short (k_icc_p, KEYMATERIAL_LENGTH); keys_p := sum_short (keySeed_p, KEYMATERIAL_LENGTH); jc_rnd_val ← jc_rnd_var.getSeq; out_array := cast_short(0); IF (jc_rnd_icc_p ≥ 0 ∧ k_icc_p ≥ 0 ∧ keySeed_p ≥ 0 ∧ keys_p ≥ 0 ∧ sum_short(jc_rnd_icc_p, RND_LENGTH) ≤ size(bf) ∧ sum_short(0, RND_LENGTH) ≤ size (jc_rnd_val) ∧ sum_short (keys_p, KEY_LENGTH) ≥ 0 ∧ mult_short(2,RND_LENGTH) ≥ 0 ∧ sum_short(mult_short(2,RND_LENGTH), KEYMATERIAL_LENGTH) ≤ size(bf) ∧ sum_short(k_icc_p, KEYMATERIAL_LENGTH) ≤ size(bf) ∧ sum_short(mult_short(2,RND_LENGTH),KEYMATERIAL_LENGTH) ≥ 0 ∧ bf 6= [] ) THEN out_array ← Util.arrayCompare (bf, jc_rnd_icc_p, jc_rnd_val, 0, RND_LENGTH); IF (out_array 6= 0) THEN ISOException.throwIt (SW_SECURITY_STATUS_NOT_SATISFIED) ELSE randomData.generateData (bf, k_icc_p, KEYMATERIAL_LENGTH); passportUtil.xor (bf, k_ifd_p, bf, k_icc_p, bf, keySeed_p, KEYMATERIAL_LENGTH); crypto.deriveKey(bf, keySeed_p, KEYMATERIAL_LENGTH, MAC_MODE, keys_p); macKey_p := keys_p; keys_p := sum_short (keys_p, KEY_LENGTH); crypto.deriveKey(bf, keySeed_p, KEYMATERIAL_LENGTH, MAC_MODE, keys_p); encKey_p := keys_p; keys_p := sum_short (keys_p, KEY_LENGTH); IF ( ¬ (macKey_p ≥ 0) ∧ ¬ (encKey_p ≥ 0) ∧ (size(bf) = 8 ∨ size(bf) = 16 ∨ size(bf) ≤ 24)) THEN keyStore.setSecureMessagingKeys( bf, macKey_p, bf, encKey_p); jc_ssc ← jc_ssc_var.getSeq; crypto.computeSSC(bf, jc_rnd_icc_p, bf, jc_rnd_ifd_p, jc_ssc); passportUtil.swap ( bf, jc_rnd_icc_p, jc_rnd_ifd_p, RND_LENGTH); res_cp ← Util.arrayCopyNonAtomic( bf, k_icc_p, bf, mult_short(2, RND_LENGTH), KEYMATERIAL_LENGTH); crypto.encryptInit; ciphertext_len ← crypto.encryptFinal( bf, cast_short (0), sum_short( mult_short(2, RND_LENGTH), KEYMATERIAL_LENGTH), bf, cast_short(0) ); IF ciphertext_len ≥ 0 THEN crypto.initMac(MODE_SIGN); crypto.createMacFinal(bf, cast_short(0), ciphertext_len, bf, ciphertext_len) ELSE ISOException.throwIt ( SW_SECURITY_STATUS_NOT_SATISFIED) END ELSE ISOException.throwIt (SW_SECURITY_STATUS_NOT_SATISFIED) END END ELSE ISOException.throwIt (SW_SECURITY_STATUS_NOT_SATISFIED) END END END END END END END; vol_state.set(MUTUAL_AUTHENTICATED); END END; data_out := bf 220 C.5 Máquina Abstrata Applet MACHINE Applet EXTENDS Object SEES JByte, JShort, Shareable, Apdu_Properties, Passport_Context, Passport_JC_FF_Context, InterfaceContext, FileSystem_Context INCLUDES app.Passport_JC CONCRETE_VARIABLES perState, volState, selectedFile, rnd, ssc INVARIANT perState ∈ JBYTE ∧ volState ∈ seq1(JBYTE) ∧ selectedFile ∈ EF_DG1_INDEX .. SOS_LOG_INDEX ∧ rnd ∈ seq1 (JBYTE) ∧ ssc ∈ seq1 (JBYTE) ∧ perState = app.jc_perState ∧ volState = app.jc_volState ∧ selectedFile = app.jc_selectedFile ∧ rnd = app.jc_rnd ∧ ssc = app.jc_ssc INITIALISATION perState := app.jc_perState || volState := app.jc_volState || selectedFile := app.jc_selectedFile || rnd := app.jc_rnd || ssc := app.jc_ssc OPERATIONS install(bArray, bOffset, bLength) = PRE bArray ∈ seq(JBYTE) ∧ bOffset ∈ JSHORT ∧ bOffset ≥ 0 ∧ bLength ∈ JBYTE ∧ bLength ≥ 0 ∧ bLength ≤ 127 THEN skip END; process(apdu) = PRE apdu ∈ APDU THEN skip END; data_out ← processAPDU (apdu_p, cla, ins, p1, p2, lc, data_in, le) = PRE apdu_p ∈ APDU ∧ cla ∈ JBYTE ∧ ins ∈ JBYTE ∧ p1 ∈ JBYTE ∧ p2 ∈ JBYTE ∧ lc ∈ JBYTE ∧ data_in ∈ seq(JBYTE) ∧ data_in 6= [] ∧ size (data_in) ≥ 1 ∧ size (data_in) ≤ 127 ∧ le ∈ JBYTE THEN SELECT ins = processSelectFile_INS ∧ app.jc_volState(1) = MUTUAL_AUTHENTICATED ∧ ¬ (app.jc_perState = LOCKED) ∧ data_in(1) ∈ EF_DG1_INDEX .. SOS_LOG_INDEX THEN app.processSelectFile (apdu_p, data_in) || data_out :∈ seq(JBYTE) WHEN ins = processReadBinary_INS ∧ app.jc_volState(1) = FILE_SELECTED THEN data_out ← app.processReadBinary(apdu_p, boolean_of_byte(p1), data_in) WHEN ins = processGetChallenge_INS ∧ app.jc_perState = HAS_MUTUAL_AUTHENTICATION_KEYS ∧ ¬ (app.jc_volState(1) = MUTUAL_AUTHENTICATED) THEN data_out ← app.processGetChallenge(apdu_p, boolean_of_byte(p1), data_in ) WHEN ins = processMutualAuthenticate_INS ∧ (app.jc_volState(1) = CHALLENGED ∧ ¬ (app.jc_volState(1) = MUTUAL_AUTHENTICATED)) THEN data_out ← app.processMutualAuthenticate (apdu_p, boolean_of_byte(p1)) ELSE data_out :∈ seq(JBYTE) END || perState := app.jc_perState || volState := app.jc_volState || selectedFile := app.jc_selectedFile || rnd := app.jc_rnd || ssc := app.jc_ssc END; 221 res ← select = CHOICE res := FALSE OR res := TRUE END; deselect = BEGIN skip END; interface ← getShareableInterfaceObject(clientAID, parameter) = PRE clientAID ∈ seq(JBYTE) ∧ parameter ∈ JBYTE THEN interface :∈ JSHORT END; register_1 = BEGIN skip END; register_2 (bArray, bOffset, bLength) = PRE bArray ∈ seq(JBYTE) ∧ bOffset ∈ JSHORT ∧ bOffset ≥ 0 ∧ bLength ∈ JBYTE ∧ bLength ≥ 5 ∧ bLength ≤ 16 THEN skip END; res ← selectingApplet = CHOICE res := FALSE OR res := TRUE END END 222 C.6 Refinamento Applet REFINEMENT JC_Applet_r REFINES Applet SEES JByte, JShort, Shareable, Apdu_Properties, Passport_Context, INCLUDES app.Passport_JC INVARIANT perState ∈ JBYTE ∧ volState ∈ seq1(JBYTE) ∧ selectedFile ∈ EF_DG1_INDEX .. SOS_LOG_INDEX ∧ rnd ∈ seq1 (JBYTE) ∧ ssc ∈ seq1 (JBYTE) ∧ perState = app.jc_perState ∧ volState = app.jc_volState ∧ selectedFile = app.jc_selectedFile ∧ rnd = app.jc_rnd ∧ ssc = app.jc_ssc INITIALISATION perState := app.jc_perState || volState := app.jc_volState || selectedFile := app.jc_selectedFile || rnd := app.jc_rnd || ssc := app.jc_ssc OPERATIONS install (bArray, bOffset, bLength) = BEGIN skip END; process(apdu) = BEGIN skip END; data_out ← processAPDU (apdu_p, cla, ins, p1, p2, lc, data_in, le) = VAR protectedAPDU IN protectedAPDU := FALSE; SELECT p1 = cast_byte (1) THEN protectedAPDU := TRUE END; SELECT ins = processSelectFile_INS THEN SELECT app.jc_volState(1) = MUTUAL_AUTHENTICATED ∧ ¬ (app.jc_perState = LOCKED) ∧ data_in(1) ∈ EF_DG1_INDEX .. SOS_LOG_INDEX THEN app.processSelectFile (apdu_p, data_in) END WHEN ins = processReadBinary_INS ∧ app.jc_volState(1) = FILE_SELECTED THEN data_out ← app.processReadBinary(apdu_p, protectedAPDU, data_in) WHEN ins = processGetChallenge_INS ∧ app.jc_perState = HAS_MUTUAL_AUTHENTICATION_KEYS ∧ ¬ (app.jc_volState(1) = MUTUAL_AUTHENTICATED) THEN data_out ← app.processGetChallenge (apdu_p, protectedAPDU, data_in) WHEN ins = processMutualAuthenticate_INS ∧ (app.jc_volState(1) = CHALLENGED ∧ ¬ (app.jc_volState(1) = MUTUAL_AUTHENTICATED)) THEN data_out ← app.processMutualAuthenticate (apdu_p, protectedAPDU) ELSE data_out := [] END END; res ← select = CHOICE res := TRUE OR res := FALSE END; deselect = BEGIN skip END; interface ← getShareableInterfaceObject(clientAID, parameter) = BEGIN interface :∈ JSHORT END; register_1 = BEGIN skip END; register_2 (bArray, bOffset, bLength) = BEGIN skip END; res ← selectingApplet = CHOICE res := FALSE OR res := TRUE END; result ← equals(obj) = CHOICE result := TRUE OR result := FALSE END END 223 C.7 Implementação Applet IMPLEMENTATION JC_Applet_i REFINES JC_Applet_r SEES JByte, JShort, ISO7816, Shareable, Classes, Apdu_Properties, Passport_Context, Passport_JC_FF_Context, InterfaceContext, FileSystem_Context IMPORTS apdu.Apdu, ISOException.ISOException, Util.Util, app.Passport_JC INVARIANT perState ∈ JBYTE ∧ volState ∈ seq1(JBYTE) ∧ selectedFile ∈ EF_DG1_INDEX .. SOS_LOG_INDEX ∧ rnd ∈ seq1 (JBYTE) ∧ ssc ∈ seq1 (JBYTE) ∧ perState = app.jc_perState ∧ volState = app.jc_volState ∧ selectedFile = app.jc_selectedFile ∧ rnd = app.jc_rnd ∧ ssc = app.jc_ssc INITIALISATION perState := PER_STATE_INIT; volState := [NO_FILE_SELECTED]; selectedFile := EF_DG1_INDEX; rnd := [0, 0, 0, 0, 0, 0, 0, 0]; ssc := [0, 0, 0, 0, 0, 0, 0, 0] OPERATIONS install ( bArray , bOffset , bLength ) = BEGIN skip END; process(apdu) = VAR bf, cla, ins, p1, p2, lc, data_in, lr, data_out, apdu_state, res, res2 IN bf ← apdu.getBuffer; cla := cast_byte (0); ins := cast_byte (0); p1 := cast_byte (0); p2 := cast_byte (0); lc := cast_byte (0); IF (size (bf) ≥ 6 ∧ bf(OFFSET_CLA) ≤ MINBYTE ∧ bf(OFFSET_CLA) ≥ MAXBYTE ∧ bf(OFFSET_INS) ≤ MINBYTE ∧ bf(OFFSET_INS) ≥ MAXBYTE ∧ bf(OFFSET_P1) ≤ MINBYTE ∧ bf(OFFSET_P1) ≥ MAXBYTE ∧ bf(OFFSET_P2) ≤ MINBYTE ∧ bf(OFFSET_P2) ≥ MAXBYTE ∧ bf(OFFSET_LC) ≤ MINBYTE ∧ bf(OFFSET_LC) ≥ MAXBYTE ) THEN cla := bf(OFFSET_CLA); ins := bf(OFFSET_INS); p1 := bf(OFFSET_P1); p2 := bf(OFFSET_P2); lc := bf(OFFSET_LC) ELSE data_in := [0]; data_out := [0]; apdu_state ← apdu.getCurrentState; IF (lc > 0) THEN IF (apdu_state = STATE_INITIAL) THEN res ← apdu.setIncomingAndReceive; res2 ← Util.arrayCopyNonAtomic (bf, OFFSET_CDATA, data_in, 0, lc) END END; lr := cast_byte(0); /∗ ===> Uncomment here to call service operations <=== data_out = processAPDU ( apdu, cla, ins, p1, p2, lc, data_in, le ); lr = data_out.length ; */ (...) IF lr > 0 THEN apdu_state ← apdu.getCurrentState; IF (lr ≥ 0 ∧ lr ≤ 256 ∧ sum_short(0, lr) ≤ BUFFER_LENGTH) THEN IF (apdu_state = STATE_INITIAL ∧ apdu_state 6= STATE_OUTGOING) THEN res ← apdu.setOutgoing ELSIF (apdu_state = STATE_OUTGOING ∧ apdu_state 6= STATE_OUTGOING_LENGTH_KNOWN) THEN apdu.setOutgoingLength(lr); apdu.sendBytes (0, lr) ELSE ISOException.throwIt (SW_SECURITY_STATUS_NOT_SATISFIED) END ELSE ISOException.throwIt (SW_SECURITY_STATUS_NOT_SATISFIED) END END END END; 224 data_out ← processAPDU ( apdu_p, cla, ins, p1, p2, lc, data_in, le ) = VAR per_st, vol_st, protectedAPDU IN per_st ← app.getPersistentState; vol_st ← app.getVolatileState; protectedAPDU := FALSE; IF ins = processSelectFile_INS THEN IF vol_st = MUTUAL_AUTHENTICATED ∧ ¬ (per_st = LOCKED) ∧ data_in(1) ∈ EF_DG1_INDEX .. SOS_LOG_INDEX THEN app.processSelectFile (apdu_p, data_in) END ELSIF ins = processReadBinary_INS THEN IF vol_st = FILE_SELECTED THEN IF p1 = cast_byte (1) THEN protectedAPDU := TRUE END; data_out ← app.processReadBinary(apdu_p, protectedAPDU, data_in ) END ELSIF ins = processGetChallenge_INS THEN IF per_st = HAS_MUTUAL_AUTHENTICATION_KEYS ∧ ¬ (vol_st = MUTUAL_AUTHENTICATED) THEN IF p1 = cast_byte (1) THEN protectedAPDU := TRUE END; data_out ← app.processGetChallenge (apdu_p, protectedAPDU, data_in ) END ELSIF ins = processMutualAuthenticate_INS THEN IF (vol_st = CHALLENGED ∧ ¬ (vol_st = MUTUAL_AUTHENTICATED)) THEN IF p1 = cast_byte (1) THEN protectedAPDU := TRUE END; data_out ← app.processMutualAuthenticate (apdu_p, protectedAPDU) END ELSE data_out := [] END END; res ← select = BEGIN res := TRUE END; deselect = BEGIN skip END; interface ← getShareableInterfaceObject ( clientAID , parameter ) = BEGIN interface := 0 END; register_1 = BEGIN skip END; register_2 ( bArray , bOffset , bLength ) = BEGIN skip END; res ← selectingApplet = BEGIN res := TRUE END; result ← equals ( obj ) = BEGIN result := FALSE END END 225 C.8 Refinamento para a verificação da conformidade entre o modelo abstrato (Passport) e o concreto (Passport_JC) REFINEMENT Passport_ref REFINES Passport SEES JByte, JShort, JBoolean, ISO7816, Apdu_Properties, Passport_Context, Passport_JC_FF_Context, FileSystem_Context, InterfaceContext INCLUDES fileSystem.FileSystem, applet.Applet INVARIANT perState = applet.perState ∧ volState = applet.volState ∧ selectedFile = applet.selectedFile ∧ rnd = applet.rnd ∧ ssc = applet.ssc INITIALISATION skip OPERATIONS processSelectFile (fid)= VAR apdu_p, cla, ins, p1, p2, lc, data, le, data_out IN apdu_p :∈ APDU; cla := CLA_NUMBER; ins := processSelectFile_INS; p1 := cast_byte (0); p2 := cast_byte (0); lc := cast_byte (1); data := seq_of_byte(fid); le := cast_byte (0); data_out ← applet.processAPDU(apdu_p, cla, ins, p1, p2, lc, data, le) END; file ← processReadBinary (protectedAPDU, le)= VAR apdu, cla, ins, p1, p2, lc, data, data_out IN apdu :∈ APDU; cla := CLA_NUMBER; ins := processReadBinary_INS; p1 := byte_of_boolean (protectedAPDU); p2 := cast_byte (0); lc := cast_byte (2); data := seq_of_short(le); SELECT size (data) ≥ 1 ∧ size (data) ≤ 127 THEN data_out ← applet.processAPDU(apdu, cla, ins, p1, p2, lc, data, cast_byte(8)); file := data_out END END; 226 rnd_val ← processGetChallenge (protectedAPDU, le) = VAR apdu, cla, ins, p1, p2, lc, data, data_out IN apdu :∈ APDU; cla := CLA_NUMBER; ins := processGetChallenge_INS; p1 := byte_of_boolean (protectedAPDU); p2 := cast_byte (0); lc := cast_byte (2); data := seq_of_short(le); SELECT size (data) ≥ 1 ∧ size (data) ≤ 127 THEN data_out ← applet.processAPDU(apdu, cla, ins, p1, p2, lc, data, cast_byte(8)); rnd_val := data_out END END; key_data ← processMutualAuthenticate (protectedAPDU) = VAR apdu, cla, ins, p1, p2, lc, data, data_out, le IN apdu :∈ APDU; cla := CLA_NUMBER; ins := processMutualAuthenticate_INS; p1 := byte_of_boolean (protectedAPDU); p2 := cast_byte (0); lc := cast_byte (0); le := cast_byte (40); data :∈ seq1(JBYTE); data_out ← applet.processAPDU(apdu, cla, ins, p1, p2, lc, data, le); key_data := data_out END; res ← getPersistentState = BEGIN res := applet.perState END; res ← getVolatileState = BEGIN res := applet.volState END END