Compilador ASN1

De MediaWiki do Campus São José
Revisão de 09h24min de 30 de setembro de 2015 por Msobral (discussão | contribs) (→‎ASN1String)
Ir para navegação Ir para pesquisar

O compilador ASN1 desenvolvido por Lev Walkin traduz as especificações ASN1 para linguagem C, além de prover uma biblioteca para codificação e decodificação. Ele é compatível com ASN.1 rev. 2002. Há duas formas de utilizá-lo:

  1. Obtendo seu código-fonte, e realizando sua compilação e instalação
  2. Acessando sua versão online

O resultado da compilação ASN1 é um conjunto de arquivos em código-fonte C, contendo a definição dos tipos de dados criados com ASN1 e as funções auxiliares para usar esses tipos de dados e realizar codificação e decodificação. Por exemplo, ao compilar esta especificação ASN1:

Exemplo DEFINITIONS ::=
BEGIN
	Mensagem ::= SEQUENCE {
   		id	VisibleString,
		cod	INTEGER,
                oid OBJECT IDENTIFIER
	}
END


... são gerados estes arquivos:

asn_application.h  +Compiler.Log       Mensagem.h           per_opentype.c
asn_codecs.h       constraints.c       module.asn1          per_opentype.h
asn_codecs_prim.c  constraints.h       NativeEnumerated.c   per_support.c
asn_codecs_prim.h  constr_SEQUENCE.c   NativeEnumerated.h   per_support.h
asn_internal.h     constr_SEQUENCE.h   NativeInteger.c      VisibleString.c
asn_system.h       constr_TYPE.c       NativeInteger.h      VisibleString.h
ber_decoder.c      constr_TYPE.h       OBJECT_IDENTIFIER.c  xer_decoder.c
ber_decoder.h      converter-sample.c  OBJECT_IDENTIFIER.h  xer_decoder.h
ber_tlv_length.c   der_encoder.c       OCTET_STRING.c       xer_encoder.c
ber_tlv_length.h   der_encoder.h       OCTET_STRING.h       xer_encoder.h
ber_tlv_tag.c      INTEGER.c           per_decoder.c        xer_support.c
ber_tlv_tag.h      INTEGER.h           per_decoder.h        xer_support.h
BIT_STRING.c       Makefile.am.sample  per_encoder.c
BIT_STRING.h       Mensagem.c          per_encoder.h

Dentre eles, os arquivos Mensagem.c e Mensagem.h contêm as declarações correspondentes à estrutura de dados Mensagem. Os demais arquivos incluem funções e declarações para o uso, codificação e decodificação de estruturas de dados especificadas via ASN1. Portanto, além de compilar uma especificação, é gerada uma API para usar as estruturas de dados criadas.


A API nativa gerada pelo compilador pode ser usada diretamente, porém ela apresenta limitações quanto ao uso dos tipos de dados básicos ASN1. Alguns tipos de dados, tais como OCTET STRING e suas variações, OBJECT IDENTIFIER e RELATIVE-OID, não estão acompanhados de funções na API para manipulá-los de forma prática. Em alguns casos é necessário criar algoritmos para acessar a estrutura interna desses tipos de dados, o que se torna repetitivo ao usá-los em programas e sujeito a erros. Além disso, as operações de codificação e decodificação também demandam uma quantidade razoável de código-fonte extra a ser escrito para poderem ser usadas. Por isso essa API gerada pelo compilador foi encapsulada em uma outra API C++, que oferece um conjunto de classes para sanar essas limitações.

API ASN1++

A nova API foi denominada ASN1++, e pode ser obtida em:


A API é composta destes arquivos:

  • asn1++.h: declarações das classes que compõem a API.
  • asn1++-impl.h: implementação dos métodos das classes
  • Makefile: script de compilação para ajudar a compilação de um projeto ASN1. Possibilita compilar a especificação ASN1 e também o projeto
  • Makefile.build: script de compilação auxiliar


Para utilizá-la basta copiar todos os arquivos nela contidos para o mesmo diretório onde está sua especificação ASN1. Caso deseje usar os scripts de compilação, edite Makefile.build e modifique-o conforme ali indicado.

Classes e classes template da API ASN1++

A API ASN1++ contém as seguintes classes e classes template:

  • ASN1DataType<T>: classe para auxiliar o uso do tipo de dados ASN1 definido pelo usuário. Definida como template para suportar qualquer tipo de dados
  • ASN1String: classe para auxiliar o acesso, conversão e modificação de strings ASN1
  • ASN1BitString: classe para auxiliar o uso de BIT STRING
  • ASN1Oid: classe para auxiliar o acesso e modificação de OBJECT IDENTIFIER
  • ASN1RelativeOid: classe para auxiliar o acesso e modificação de RELATIVE OID (um caso especial de OID). Tem mesmas operações que ASN1Oid.
  • ASN1Sequence<T>: classe para facilitar o uso de sequências ASN1 (SEQUENCE OF e SET OF). Definida como template pois o tipo do dado contido em sequências é genérico
  • ASN1DERSerializer<T>: codificador DER. Codifica estruturas de dados e grava o resultado em um stream (ex: ofstream ou ostringstream)
  • ASN1XERSerializer<T>: codificador XER. Codifica estruturas de dados e grava o resultado em um stream (ex: ofstream ou ostringstream)
  • ASN1DERDeserializer<T>: decodificador DER. Decodifica octetos lidos de um stream (ex: ifstream ou istringstream) e gera uma estrutura de dados.
  • ASN1XERDeserializer<T>: decodificador XER. Decodifica octetos lidos de um stream (ex: ifstream ou istringstream) e gera uma estrutura de dados.


PTC-Asn1++.png


ASN1DataType<T>

A classe ASN1DataType<T> encapsula uma estrutura de dados do tipo T, que deve ter sido gerada pelo compilador ASN1.


Métodos:

  • ASN1DataType(asn_TYPE_descriptor_t * desc): construtor da classe, o qual deve receber uma referência a um descritor do tipo de dados ASN1 (se o tipo de dados se chamar Mensagem, seu descritor se chama asn_DEF_Mensagem). O descritor é automaticamente declarado no código-fonte gerado pelo compilador ASN1. Este construtor cria automaticamente a estrutura de dados encapsulada de tipo T.
  • ASN1DataType(asn_TYPE_descriptor_t * desc, T * apkt): este construtor difere do anterior apenas na possibilidade de passar como parâmetro uma estrutura de dados de tipo T já existente, para que seja encapsulada.
  • T * get_data(): retorna uma referência para a estrutura de dados encapsulada
  • asn_TYPE_descriptor_t * get_desc(): retorna uma referência ao descritor do tipo de dados ASN1
  • void show(): mostra na saída padrão uma representação textual da estrutura de dados encapsulada
  • void check_constraints(): verifica se os valores dos membros da estrutura de dados encapsulada respeitam as restrições declaradas na especificação ASN1.

ASN1String

A classe ASN1String encapsula uma referência a uma string ASN1 (OCTET_STRING_t *), e implementa operações para modificar e acessar essa string.


Métodos:

  • ASN1String(OCTET_STRING & astr): construtor da classe. O parâmetro astr deve ser um membro string de uma estrutura de dados ASN1.
  • string str(): retorna uma string C++ com o conteúdo da string ASN1
  • ASN1String & operator=(const char * s): possibilita atribuir uma string C à string ASN1
  • ASN1String & operator=(const string * s): possibilita atribuir uma string C++ à string ASN1
  • ASN1String & operator+=(const char * s): possibilita anexar uma string C à string ASN1
  • ASN1String & operator+=(const string & s): possibilita anexar uma string C++ à string ASN1


Exemplo de uso:

  ASN1DataType<Mensagem_t> pkt(&asn_DEF_Mensagem);
  Mensagem_t * msg = pkt.get_data();

  ASN1String name(msg->nome);
  name = "CONTROL";
  cout << "nome: " << name.str() << endl;

ASN1BitString

ASN1Oid e ASN1RelativeOid

ASN1Sequence<T>

ASN1DERSerializer<T> e ASN1XERSerializer<T>

ASN1DERDeserializer<T> e ASN1XERDeserializer<T>

Guia rápido para a biblioteca de codificação

  • Acesso a membros do tipo INTEGER e BOOLEAN: basta referenciá-los diretamente, como neste exemplo aplicado à estrutura de dados Ativo:
    Ativo_t * pkt = (Ativo_t*)malloc(sizeof(Ativo_t));
    
    pkt->codigo = 555;
    
  • Conversão de string C para OCTET STRING:
    int OCTET_STRING_fromString(OCTET_STRING_t *s, const char *str);
    
  • Conversão de OCTET STRING para string C:
    // "ptr" é do tipo OCTET_STRING_t *
    // "data" é do tipo char*, e deve ter capacidade >= ptr->size+1
    memcpy(data, ptr->buf, ptr->size);
    data[ptr->size] = 0;
    


Para membros da estrutura de dados que forem SET OF ou SEQUENCE OF (i.e. listas de valores), usa-se esta definição de estrutura de dados como exemplo:

ASN.1 Linguagem C
Ativo ::= SEQUENCE {
  nome PrintableString (SIZE(1..16)),
  codigo INTEGER,
  valor SET OF INTEGER
}
/* Ativo */
typedef struct Ativo {
        PrintableString_t        nome;
        long     codigo;
        struct valor {
                A_SET_OF(long) list;
                
                /* Context for parsing across buffer boundaries */
                asn_struct_ctx_t _asn_ctx;
        } valor;
        
        /* Context for parsing across buffer boundaries */
        asn_struct_ctx_t _asn_ctx;
} Ativo_t;
  • Anexar um valor a uma SEQUENCE OF ou SET OF:
    // "pkt" é do tipo Ativo_t*
    int * p;
    
    p = (int*)malloc(sizeof(int));
    asn_set_add(&pkt->valor, p);
    
  • Remover um valor de uma SEQUENCE OF ou SET OF:
    // remove o valor na posição "0" da sequência
    // "valor" da estrutura de dados Ativo_t apontada por "pkt"
    // o último parâmetro informa se memória do dado removido deve ser liberada (0=não, 1=sim)
    asn_set_del(&pkt->valor, 0, 0);
    
  • Esvazia uma SEQUENCE OF ou SET OF:
    // remove todos os dados da sequência "valor"
    // liberando suas áreas de memória
    asn_set_empty(&pkt->valor);
    
  • Acesso a um dado de uma SEQUENCE OF ou SET OF:
    // n: posição acessada no vetor interno de dados da sequência
    // k: posição lógica do dado acessado (equivalente à usada em asn_set_del)
    int n, k;
    
    for (n=0, k=0; (k < pkt->valor.list.count) && (n < pkt->valor.list.size);) {
      if (pkt->valor.list.array[n] != NULL) {
        long * number = pkt->valor.list.array[n];
    
        printf("valor[%d]=%d\n", k, *number);
        k++;
      }
    }
    
  • Codificar com BER
  • Codificar com DER
  • Codificar com PER
  • Codificar com XER