i2d() -- SSLeay 0.9.0b -- January 1999

NAME

i2d, d2i -- ASN.1 conversion to and from DER-encoded form

SYNOPSIS

int i2d_TYPE(a, pp)
TYPE *a;
unsigned char **pp;

TYPE *d2i_TYPE(a, pp, length)
TYPE **a;
unsigned char **pp;
long length;

where TYPE is one of:

ASN1_BIT_STRING
ASN1_BOOLEAN  
ASN1_HEADER 
ASN1_IA5STRING
ASN1_INTEGER
ASN1_OBJECT 
ASN1_OCTET_STRING
ASN1_PRINTABLE
ASN1_TYPE 
ASN1_UTCTIME

int M_i2d_TYPE(a, pp)
TYPE *a;
unsigned char **pp; M_i2d_TYPE

TYPE *M_d2i_TYPE(a, pp, length)
TYPE **a;
unsigned char **pp;
long length;

where TYPE is one of:

ASN1_BMPSTRING
ASN1_GENERALSTRING 
ASN1_IA5STRING 
ASN1_OCTET_STRING 
ASN1_PRINTABLE 
ASN1_PRINTABLESTRING 
ASN1_T61STRING 
ASN1_UNIVERSALSTRING 

int i2d_ASN1_SET(a,pp,func,ex_tag,ex_class)
STACK *a;
unsigned char **pp;
int (*func)(), ex_tag, ex_class;

STACK *d2i_ASN1_SET(a,pp,length,func,ex_tag,ex_class)
STACK **a;
unsigned char **pp, *(*func)();
long length;
int ex_tag, ex_class;

int i2d_ASN1_bytes(a, pp, tag, xclass)
ASN1_STRING *a;
unsigned char **pp;
int tag, xclass;

ASN1_STRING *d2i_ASN1_bytes(a, pp, length, Ptag, Pclass)
ASN1_STRING **a;
unsigned char **pp;
long length;
int Ptag;
int Pclass;

ASN1_STRING *d2i_ASN1_type_bytes(a, pp, length, type)
ASN1_STRING **a;
unsigned char **pp;
long length;
int type;

DESCRIPTION

i2d_TYPE converts an ASN.1 object in an internal standardized form to its DER encoding; d2i_TYPE converts an ASN.1 object from its DER encoded form to its internal standardized form.

Other TYPEs are available but those are not listed here; you will find references to them in documents on other parts of the library. They all work exactly as described here, however.

See ASN.1 introduction and overview for much more detail on DER-encoding than you probably want to know. You really should review that section if you haven;t yet, because parts of this document assume that you are at least generally familiar with the Distinguished Encoding Rules.

The d2i functions copy a binary representation into an internal C structure, as follows:

ais a pointer to a pointer to the structure to populate, pp is a pointer to a pointer to the DER byte string, and length is the length of the *pp data.

If a or *a is NULL, *a will be set to point to a newly created structure, with space for *a allocated if a was NULL. If *a is not NULL, the results of the conversion will be placed into the structure it points to.

On successful completion, *pp will be updated to point to *pp + length, so that d2i can be called repeatedly with the same arguments to process a block of DER-encoded objects. The address of the populated structure will be returned.

On error, d2i returns NULL, and if *a was not NULL, the structure it points to is freed.

Errors occur if memory for the new structure cannot be allocated, or if the DER-encoded data has syntax errors.

The i2d functions convert a structure into a DER-encoded object and stuf it into a character string as follows:

ais a pointer to a pointer to the structure to convert, and pp is a pointer to a pointer to the DER byte string to be created.

On successful completion, *pp will be updated to point to the end of the character string just created, so that i2d can be called repeatedly on a collection of structures to create one long DER-encoded string. The length of the character string will be returned.

If pp is NULL, the length of the string that would have been created will still be returned but the string itself will not be put anywhere. This feature can be used to determine the number of bytes to malloc for a character string in the first call to the function; the next call would pass a pointer to the string, like this:

len=i2d_my_favorite_type(a,NULL);
if ((string=(char *)malloc(len)) == NULL) complain...
i2d_my_favorite_type(a,&string);

On error, or if a is NULL, 0 will be returned.

Let's look at an example.

We'll take the BIT STRING '01000100111011' from the introduction to this library.

Its encoding is

03 03 02 44 ec

where byte 1 is the identifier octet, byte 2 is length and bytes 3-5 are content.

To convert this to an internal C structure, d2i_ASN1_BIT_STRING does the following:

allocates a structure, if necessary. the internal structure for a BIT STRING is:

typedef struct asn1_bit_string2_st
        {
        int length;
        int type;
        unsigned char *data;
        } ASN1_BIT_STRING;

Then it calls ASN1_get_object which does things like pull apart the length, get the object identifier, etc. It also checks the object identifier to make sure that the identifier really is the one for ASN1_BIT_STRING. Then it puts the length into the length element of the structure, the content bytes (except for the first one which is the number of extra bits) into the data element, makes sure that the 'extra bits' at the end are zero, and returns a pointer to the structure.

Note that it does not shift the bitstring to the right; you are left with the zero padding.

The moral of this story is that bit strings in C are always multiples of 8 bytes.

Note also that the data element is not null-terminated.

The i2d_ASN1_BIT_STRING function copies an object in a ASN1_BIT_STRING structure (*a) into a DER-encoded bytestring (*pp) as follows:

The data element of the structure is of course an even multiple of 8 bits, so there is no padding. It calls ASN1_put_object with argument V_ASN1_BIT_STRING, which sets up and writes out the identifier octet, computes the length and writes it out, and writes out the data as content bytes as discussed above.

pp will be updated to point to the next byte after the last one written. This is so that multiple calls to the function can be made in order to DER-encode a collection of ASN1 objects into one continuous block of data.

If pp is NULL, no data is written, but the function still returns the length of the data it would write. This can be used to allocate the proper amount of memory for pp before calling the function again with a non-NULL pointer, like this:

int len;
unsigned char *bytes,*p;
len=i2d_X509(x,NULL);   /* get the size of the ASN1 encoding of 'x' */
if ((bytes=(unsigned char *)malloc(len)) == NULL)
        goto err;
p=bytes;
i2d_X509(x,&p);
Please note that &p, and not &bytes, was passed to i2d_X509, since it will be incremented by len bytes after the call to i2d_X509. In order to do further operations on the X509 object, you must keep the original value of the pointer where the structure is to be placed, in this case, bytes.

SETs and SEQUENCESs

These functions process ASN1_SETs and ASN1_SEQUENCEs.

i2d_ASN1_SET acts just like the other i2d functions, except that it takes three more arguments, an ex_tag which is one of the following:

V_ASN1_SET
V_ASN1_SEQUENCE
a context-specific value

an ex_class which is one of the following:

V_ASN1_UNIVERSAL
V_ASN1_CONTEXT_SPECIFIC

and a func i2d_something which is called to operate on each element of the STACK which a points to. The STACK contains the ASN1 structures that will compose the SET (or SEQUENCE), in some order. For example, the STACK could contain a bunch of X509_ATTRIBUTEs; then you might call

i2d_ASN1_SET(sk,&p,i2d_X509_ATTRIBUTE,V_ASN1_SET,V_ASN1_UNIVERSAL)

to get the DER-encoded form of the SET.

d2i_ASN1_SET acts just like the other d2i functions, except that it takes three more arguments, an ex_tag and an ex_class, as defined above for i2d_ASN1_SET; and and a func d2i_something which is called to operate on each element of the DER-encoded SET or SEQUENCE and create the appropriate ASN1 structure which is then put on the stack **a and a pointer to it returned.

Null is returned on error.

Macros for ASN1_STRINGs

Since the various types of character strings are all really ASN1_STRINGs underneath, a set of macros is provided to manipulate them, translating each one to a function call with its proper tag and class.

These M_i2d_ and M_d2i_ functions work just like the regular i2d and d2i functions. Their definitions are given below:

I kinda wonder if there isn't a typo in that first one and it shouldn't read ...V_ASN1_OCTET_STRING,V_ASN1_UNIVERSAL) but that's the way it goes.

#define M_i2d_ASN1_OCTET_STRING(a,pp) 
   i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_OCTET_STRING,V_ASN1_OCTET_STRING)

#define M_i2d_ASN1_PRINTABLE(a,pp) 
   i2d_ASN1_bytes((ASN1_STRING *)a,pp,a->type,V_ASN1_UNIVERSAL)
#define M_d2i_ASN1_PRINTABLE(a,pp,l) 
   d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l, 
      B_ASN1_PRINTABLESTRING| B_ASN1_T61STRING| B_ASN1_IA5STRING| 
      B_ASN1_BIT_STRING| B_ASN1_UNIVERSALSTRING| B_ASN1_BMPSTRING|
      B_ASN1_UNKNOWN)

#define M_i2d_ASN1_PRINTABLESTRING(a,pp) 
   i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_PRINTABLESTRING,V_ASN1_UNIVERSAL)
#define M_d2i_ASN1_PRINTABLESTRING(a,pp,l) 
   (ASN1_PRINTABLESTRING *)d2i_ASN1_type_bytes ((ASN1_STRING **)a,pp,l,B_ASN1_PRINTABLESTRING)

#define M_i2d_ASN1_T61STRING(a,pp) 
   i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_T61STRING,V_ASN1_UNIVERSAL)
#define M_d2i_ASN1_T61STRING(a,pp,l) 
   (ASN1_T61STRING *)d2i_ASN1_type_bytes ((ASN1_STRING **)a,pp,l,B_ASN1_T61STRING)

#define M_i2d_ASN1_IA5STRING(a,pp)
   i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_IA5STRING,V_ASN1_UNIVERSAL)
#define M_d2i_ASN1_IA5STRING(a,pp,l) 
   (ASN1_IA5STRING *)d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l,B_ASN1_IA5STRING)

#define M_i2d_ASN1_GENERALSTRING(a,pp) 
   i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_GENERALSTRING,V_ASN1_UNIVERSAL)
#define M_d2i_ASN1_GENERALSTRING(a,pp,l) 
   (ASN1_GENERALSTRING *)d2i_ASN1_type_bytes ((ASN1_STRING **)a,pp,l,B_ASN1_GENERALSTRING)

#define M_i2d_ASN1_UNIVERSALSTRING(a,pp) 
   i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_UNIVERSALSTRING,V_ASN1_UNIVERSAL)
#define M_d2i_ASN1_UNIVERSALSTRING(a,pp,l) 
   (ASN1_UNIVERSALSTRING *)d2i_ASN1_type_bytes ((ASN1_STRING **)a,pp,l,B_ASN1_UNIVERSALSTRING)

#define M_i2d_ASN1_BMPSTRING(a,pp) 
   i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_BMPSTRING,V_ASN1_UNIVERSAL)
#define M_d2i_ASN1_BMPSTRING(a,pp,l) 
   (ASN1_BMPSTRING *)d2i_ASN1_type_bytes ((ASN1_STRING **)a,pp,l,B_ASN1_BMPSTRING)

Note that both the macro M_i2d_ASN1_OCTET_STRING and the function i2d_ASN1_OCTET_STRING are provided, while only d2i_ASN1_OCTET_STRING is provided for the reverse direction. Similarly, there is a d2i_ASN1_PRINTABLESTRING but no corresponding i2d function, only a macro; likewise, there is a d2i_ASN1_T61STRING function but only a macro for the reverse.

The following table lists the internal structure names that these functions manipulate and the corresponding ASN.1 type names, for those who are foolhardy enough to wade through the standards.

internal C structurecorresponding ASN.1 type
ASN1_BIT_STRINGBIT STRING
ASN1_BMPSTRINGBMPString
ASN1_GENERALIZEDTIMEGeneralizedTime
ASN1_GENERALSTRINGGeneralString
ASN1_INTEGERINTEGER
ASN1_OCTET_STRINGOCTET STRING
ASN1_OBJECTOBJECT IDENTIFIER
ASN1_PRINTABLESTRINGPrintableString
ASN1_T61STRINGT61String
ASN1_IA5STRINGIA5String
ASN1_TYPEAny of the above mentioned types
plus SEQUENCE and SET
ASN1_UNIVERSALSTRINGUniversalString
ASN1_UTCTIMEUTCTime

Oddities about i2d/d2i functions

Note that no structure is provided for ASN1_BOOLEAN even though there are conversion functions for this type; the internal storage form is an integer.

Similarly, ASN1_SET has conversion functions but no structure definition; its internal form is a STACK of whatever happens to be in the SET.

And finally, ASN1_SEQUENCE has neither conversion functions nor a structure. How do we manipulate all of these X509 objects filled with SEQUENCE OF { ... } ? We are provided with a set of macros to do the job; see ASN.1 macros for more information.

Note that i2d_ASN1_GENERALIZEDTIME isn't written yet; neither is d2i_ASN1_GENERALIZE_TIME, although there are structures for each of these types.

Also, note that T61String and TeletexString are the names for the same object in the literature, so if you see references to TeletexString in some spec and can't find it in the above table, look for T61String instead, and vice versa.

Lower-level functions

The following functions are lower-level functions that you could use to construct new i2d or d2i functions; otherwise you probably won't need to call them.

i2d_ASN1_bytes acts just like the other i2d functions, except that it takes two more arguments, a tag a which is one of the following:

V_ASN1_EOC
V_ASN1_BOOLEAN
V_ASN1_INTEGER
V_ASN1_NEG_INTEGER
V_ASN1_BIT_STRING
V_ASN1_OCTET_STRING
V_ASN1_NULL
V_ASN1_OBJECT
V_ASN1_OBJECT_DESCRIPTOR
V_ASN1_EXTERNAL
V_ASN1_REAL
V_ASN1_ENUMERATED
V_ASN1_SEQUENCE
V_ASN1_SET
V_ASN1_NUMERICSTRING
V_ASN1_PRINTABLESTRING
V_ASN1_T61STRING         (or V_ASN1_TELETEXSTRING)
V_ASN1_VIDEOTEXSTRING
V_ASN1_IA5STRING
V_ASN1_UTCTIME
V_ASN1_GENERALIZEDTIME
V_ASN1_GRAPHICSTRING
V_ASN1_ISO64STRING       (or V_ASN1_VISIBLESTRING)
V_ASN1_GENERALSTRING
V_ASN1_UNIVERSALSTRING
V_ASN1_BMPSTRING

and a class xclass which is one of:

V_ASN1_UNIVERSAL
V_ASN1_APPLICATION
V_ASN1_CONTEXT_SPECIFIC
V_ASN1_PRIVATE

It uses these to write the identifier octet. NOTE THAT this function only encodes ASN1_STRINGs of one sort or another properly; don't try to give it a tag for an ASN1_INTEGER, for example, because you will get a garbage encoding back.

d2i_ASN1_bytes acts just like the other d2i functions except that it takes two additional arguments, a pointer to a tag Ptag and a pointer to a class Pclass, where the tag and the class from the identifier octet, as decsribed above, will be placed. NOTE THAT this function only parses things that are ASN1_STRINGs of one sort or another, and it returns a pointer to an ASN1_STRING.

d2i_ASN1_type_bytes acts just like the other d2i functions except that it takes one additional argument, type, which is a composite of predefined values (they can be ORed together) from the list below:

B_ASN1_NUMERICSTRING
B_ASN1_PRINTABLESTRING
B_ASN1_T61STRING
B_ASN1_VIDEOTEXSTRING
B_ASN1_GRAPHICSTRING
B_ASN1_ISO64STRING
B_ASN1_GENERALSTRING
B_ASN1_UNIVERSALSTRING
B_ASN1_OCTET_STRING
B_ASN1_BIT_STRING
B_ASN1_BMPSTRING
B_ASN1_UNKNOWN

The tag as retrieved from the object is compared with the tag corresponding to the type passed by the user; if they don't match, an error is flagged and NULL is returned. NOTE THAT this function only parses things that are ASN1_STRINGs of one sort or another, and it returns a pointer to an ASN1_STRING.