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;
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.
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.
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 structure | corresponding ASN.1 type |
ASN1_BIT_STRING | BIT STRING |
ASN1_BMPSTRING | BMPString |
ASN1_GENERALIZEDTIME | GeneralizedTime |
ASN1_GENERALSTRING | GeneralString |
ASN1_INTEGER | INTEGER |
ASN1_OCTET_STRING | OCTET STRING |
ASN1_OBJECT | OBJECT IDENTIFIER |
ASN1_PRINTABLESTRING | PrintableString |
ASN1_T61STRING | T61String |
ASN1_IA5STRING | IA5String |
ASN1_TYPE | Any of the above mentioned types plus SEQUENCE and SET |
ASN1_UNIVERSALSTRING | UniversalString |
ASN1_UTCTIME | UTCTime |
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.
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_UNKNOWNThe 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.