Desenvolvimento Formal de Aplicações para Smart Cards

Propaganda
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
Download