The Ubiquitous asn__any

It is impossible to do almost anything with SETREF without running into the asn__any datatype. With few exceptions, all datatypes but asn__any contain C-native data. asn__any is unique in that it contains DER-encoded data. asn__any is used by SETREF for hiding the complexity of arbitrarily complex data behind the opaque ASN.1 ANY data type, leaving DER-encoded data in its native form as an optimization, and treating conceptually automomous objects as independently-encoded data units.

The type declaration for asn__any is

typedef asn__any_ {
    UINT4   length;
    UCHAR  *object;   /* contains the tag and length octets */
} asn__any;
The difference between an C-native integer and a DER-encoded integer is shown in the following example.
int       C_integer    = 5;                      /* int 5 */
asn__any  DER_integer  = { 3, "\002\001\005" };  /* DER-encoded int 5 */
In the case of the DER_integer, '\002' specifies that it is an integer, '\001' specifies that the integer is encoded as 1-byte long, and '\005' specifies that the value of the integer is 5. Don't worry, SETREF provides functions to convert between C-native and DER-encoded datatypes. Most importantly, SETREF insulates the application programmer having to make these conversions (a procedure which is often called data marshalling). DER encoding transforms arbitrarily complex C-native structures and datatypes into arrays of bytes.

One reason for using data in DER-encoded form is that some data structures are rarely, if ever, needed in decoded form. Moreover, the decoded form may be complex and difficult to manage. For instance, X.509 Distinguished Names (DNs) decode into rather boroque structures (in C, this is often a struct containing a union containing a linked-list of linked-lists of type-value pairs). Fortunately, Distinguished Names can often be used in their DER-encoded form, which is just an array of bytes. For instance, Distinguished Names are used as database lookup keys. It's difficult to imagine a database that would support native lookup keys of type "struct containing a union containing a linked-list of linked-lists of type-value pairs", so instead, the canonicalized DER-encoded Distinguished Name is used as the key. Since Distinguished Names rarely need to be decoded, it is easier to threat them in DER-encoded form--as array of bytes--which is precisely the semantics of asn__any.

Another reason for using asn__any is to perform the same operation on dissimilar ASN.1 datatypes. For instance, SET defines a cryptographic hash operator H{} which can be used to compute the hash of dissimilar datatypes. In order to perform this operation, H{} takes as an argument a value of any type (which is known in ASN.1, not coincidentally, as type "ANY").

Assume that type asn__any didn't exist and that we need to write code to implement H{} that would compute the cryptographic hash of the following types A and B.

typedef struct {
    int   i;
    int   j;
} A;

typedef double B;
Such a function H() might look something like the following.
#define TYPE_A  1
#define TYPE_B  2

int H(void *input, int input_type, hash output)
{
    start_hash(output);
    
    switch (input_type) {
    case TYPE_A:
        /* compute the hash of int A.i */
        add_hash(&(((A*)input)->i), sizeof(int), output);
        /* compute the hash of int A.j */
        add_hash(&(((A*)input)->j), sizeof(int), output);
        break;
    case TYPE_B:
        /* compute the hash of a double */
        add_hash((double*)input, sizeof(double), output);
        break;
    };
    
    finish_hash(output);
    
    return 0;        
}
This is fine, but what happens when someone declares types C, D, and so forth? And what happens when that same person wants to compute a hash of a Distinguished Name? This is where asn__any comes in. The function H() becomes the following.
int H(asn__any *input, hash output)
{
    start_hash(output);
    add_hash(input->object, input->length, output);        
    finish_hash(output);
    return 0;        
}
Now, hashing new datatypes does not require changing H(). Any datatype defined by SET can be marshalled into (and out of) C-native form using the ASN.1/DER runtime, specifically using the functions decode_using_any() and encode_using_any().

While SETREF makes heavy use of asn__any, the application programmer rarely needs to decode these. Typically, the application can treat this data as opaque.


Copyright © 1996, 1997, Visa International Service Association and MasterCard International Incorporated
All Rights Reserved.