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 :
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 <stdio.h> #include <stdlib.h> #include <errno.h> #include <stdint.h> #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;i<INTARRAYSIZE;i++) printf("%d ",Pkt2.myInts[i]); printf("\n"); if (Pkt.myName) free (Pkt.myName); if (Pkt2.myName) free (Pkt2.myName); return 0; } /* * Encodes a MyPacket structure into DER data * Returns the size of the resulting DER data */ u8 Encode_DER_Packet(MyPacket * Pkt, u8 * buf) { u8 oid[] = { 1, 3, 6, 1, 5, 9, 0, 0, 0, 0 }; /* * MyPacket_t and MyInt_t declared in MyPacket.h and MyInt.h */ MyPacket_t *myPacket; MyInt_t *myInt; asn_enc_rval_t er; /* Encoder return value */ u8 ret, i; myPacket = calloc(1, sizeof *myPacket); /* * Fill in myObjectId so that the other end knows which * protocol is this */ ret = OBJECT_IDENTIFIER_set_arcs(&myPacket->myObjectId, 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

Comment