~~NOTOC~~
~~NOCACHE~~
====== ASN.1 DER encoding and decoding in C - example/tutorial ======
This article provides a simple example of howto specify a new protocol using ASN.1 and how to encode and decode the new protocol structures (or packets) to and from DER.
===== What is ASN1 =====
For background information about ASN1 :
* [[wp>Abstract Syntax Notation One]]
* [[http://lionet.info/asn1c/|ASN.1C, free, open source ASN.1 to C compiler]]
===== An ASN.1 DER example project =====
==== 1. Create the ASN.1 specification of the new protocol (MyProto)====
Place the following into a file e.g. MyProto.asn1
-- Our MyProto specification defines a new security protocol
-- Note that all identifiers use must start with a lowercase character
MyProto {
iso(1) identified-organization(3) dod(6) internet(1) security(5) myproto(9)
} DEFINITIONS ::= BEGIN
MyPacket ::= SEQUENCE {
myObjectId OBJECT IDENTIFIER,
myInts SEQUENCE OF MyInt,
myName IA5String,
myFlags BIT STRING {
firstFlag(0),
secondFlag(1),
thirdFlag(2),
fourthFlag(3),
fifthFlag(4),
sixthFlag(5),
seventhFlag(6),
eighthFlag(7) }
}
MyInt ::= INTEGER (0..65535)
END
==== 2. Compile the protocol specification to produce .c and .h helper files ====
Install the **asn1c** compiler if needed, then compile the ASN.1 specification as follows (assuming the ASN.1 specification is in the file MyProto.asn1)
asn1c MyProto.asn1
==== 3. Write functions to encode and decode packets in DER ====
The code below defines two functions
uint8_t Encode_DER_Packet(MyPacket * Pkt, uint8_t * buf);
uint8_t Decode_DER_Packet(uint8_t * buf, MyPacket * Pkt);
These functions DER encode and decode data structures of type //MyPacket// (defined in the code below). The main function creates a structure of type //MyPacket//, encode using //Encode_DER_Packet// then decode it using //Decode_DER_Packet//.
#include
#include
#include
#include
#include "MyPacket.h"
#define INTARRAYSIZE 10
#define MAXPKTSIZE 256
typedef uint8_t u8;
/* Create a structure that matches the main ASN.1 declaration */
typedef struct MyPacket_ {
u8 myFlags;
u8 myInts[INTARRAYSIZE];
char *myName;
} MyPacket;
u8 Encode_DER_Packet(MyPacket * Pkt, u8 * buf);
u8 Decode_DER_Packet(u8 * buf, MyPacket * Pkt);
void print_byte(u8 byte);
int
main()
{
MyPacket Pkt,Pkt2;
u8 buf[MAXPKTSIZE];
int i;
/*
* Fill in a packet
*/
for (i = 0; i < INTARRAYSIZE; i++)
Pkt.myInts[i] = i * 11 + 5;
/*
* Set the first, second and 4th bits
*/
Pkt.myFlags = 0;
Pkt.myFlags |= (1 << 0);
Pkt.myFlags |= (1 << 1);
Pkt.myFlags |= (1 << 3);
/*
* Set the myName string
*/
Pkt.myName = calloc(1, 256);
strcpy(Pkt.myName, "Mister Z");
/*
* Encode Pkt into DER
*/
i = Encode_DER_Packet(&Pkt, buf);
/*
* Decode DER data to Pkt2
*/
i = Decode_DER_Packet(buf,&Pkt2);
/*
* Display contents of Pkt2
*/
printf("\n\nStructure Pkt2: \n");
printf("\nMyFlags : ");
print_byte(Pkt2.myFlags);
printf("\nMyName : %s", Pkt2.myName);
printf("\nMyInts : ");
for (i=0;imyObjectId, oid,
sizeof(oid[0]),
sizeof(oid) / sizeof(oid[0]));
if (ret != 0)
goto fail;
/*
* Fill in myInts
*/
for (i = 0; i < INTARRAYSIZE; i++)
{
myInt = calloc(1, sizeof *myInt);
*myInt = Pkt->myInts[i];
ret = ASN_SEQUENCE_ADD(&myPacket->myInts, myInt);
if (ret != 0)
goto fail;
}
/*
* Fill in myName
*/
myPacket->myName.buf = calloc(1, strlen(Pkt->myName) + 1);
strcpy(myPacket->myName.buf, Pkt->myName);
myPacket->myName.size = strlen(Pkt->myName);
/*
* Fill in myFlags
*/
myPacket->myFlags.buf = calloc(1, 1);
myPacket->myFlags.size = 1 /* 1 byte */ ;
for (i = 0; i < 8; i++)
if (Pkt->myFlags & (1 << i))
myPacket->myFlags.buf[0] |= (1 << (7 - i));
/*
* Encode to DER and place result in buf
*/
er = der_encode_to_buffer(&asn_DEF_MyPacket, myPacket, buf,
MAXPKTSIZE);
if (er.encoded == -1)
{
fprintf(stderr, "Cannot encode %s: %s\n",
er.failed_type->name, strerror(errno));
goto fail;
}
else
{
printf("Structure successfully encoded\n");
/*xer_fprint(stdout, &asn_DEF_MyPacket, myPacket);*/
asn_DEF_MyPacket.free_struct (&asn_DEF_MyPacket, myPacket, 0);
return er.encoded;
}
fail:
fprintf(stderr, "Cannot encode %s: %s\n", er.failed_type->name, strerror(errno));
asn_DEF_MyPacket.free_struct(&asn_DEF_MyPacket, myPacket, 0);
return -1;
}
/*
* Decodes a DER encoded data into a MyPacket structure
* Returns the size of the resulting MyPacket structure
*/
u8
Decode_DER_Packet(u8 * buf, MyPacket * Pkt)
{
u8 *oid;
u8 oid_size;
/*
* MyPacket_t and MyInt_t declared in MyPacket.h and MyInt.h
*/
MyPacket_t *myPacket = 0;
MyInt_t *myInt;
asn_dec_rval_t er; /* decoder return value */
u8 ret, i;
/*
* Decode the buffer into the myPacket structure
* The asn1c compiler provides the generic BER decoder which
* is implicitly capable of decoding BER, CER and DER encoded data.
*/
er = ber_decode(0, &asn_DEF_MyPacket, (void **) &myPacket, buf,
MAXPKTSIZE);
if (er.code != RC_OK)
goto fail;
else
{
printf("Structure successfully decoded\n");
/*xer_fprint(stdout, &asn_DEF_MyPacket, myPacket);*/
}
/*
* Extract the OBJECT_IDENTIFIER into oid / oid_size
*/
oid_size = OBJECT_IDENTIFIER_get_arcs(&myPacket->myObjectId, 0,
sizeof(oid[0]), 0);
oid = malloc(oid_size * sizeof(oid[0]));
if (oid) {
(void) OBJECT_IDENTIFIER_get_arcs(&myPacket->myObjectId, oid,
sizeof(oid[0]), oid_size);
free (oid);
oid = NULL;
}
/*
* Extract MyInts
*/
for (i = 0; i < myPacket->myInts.list.count; i++)
Pkt->myInts[i] = *myPacket->myInts.list.array[i];
/*
* Extract MyFlags
*/
Pkt->myFlags = 0;
for (i = 0; i < 8; i++)
if (myPacket->myFlags.buf[0] & (1 << i))
Pkt->myFlags |= (1 << (7 - i));
/*
* Extract myName
*/
Pkt->myName = calloc(1, myPacket->myName.size + 1);
strcpy(Pkt->myName, myPacket->myName.buf);
asn_DEF_MyPacket.free_struct (&asn_DEF_MyPacket, myPacket, 0);
return er.consumed;
fail:
fprintf(stderr, "Decode failure %s\n", strerror(errno));
asn_DEF_MyPacket.free_struct(&asn_DEF_MyPacket, myPacket, 0);
return -1;
}
void print_byte(u8 byte){
u8 i;
for (i=0; i<8;i++)
if (byte & (1 << i) )
printf("1");
else
printf("0");
}
==== 4. Compile and run ====
Place the code above into a .c file then run the following
$ cc -o test -I. *.c
$ ./test
Structure successfully encoded
Structure successfully decoded
Structure Pkt2:
MyFlags : 11010000
MyName : Mister Z
MyInts : 5 16 27 38 49 60 71 82 93 104
===== References =====
* [[wp>Abstract Syntax Notation One]]
* [[http://lionet.info/asn1c/|ASN.1C, free, open source ASN.1 to C compiler]]
{{tag>coding howto}}