ttab.tex   [plain text]


% file: .../doc/ttab.tex

% $Header: /cvs/Darwin/Security/SecuritySNACCRuntime/doc/ttab.tex,v 1.1.1.1 2001/05/18 23:14:10 mb Exp $
% $Log: ttab.tex,v $
% Revision 1.1.1.1  2001/05/18 23:14:10  mb
% Move from private repository to open source repository
%
% Revision 1.1.1.1  1999/03/16 18:05:55  aram
% Originals from SMIME Free Library.
%
% Revision 1.1  1997/01/01 22:47:43  rj
% first check-in
%

\chapter{\label{ttab-chapter}Type Tables}

Type tables are a flexible and compact way of dealing with ASN.1.
The type table data structure is included in the appendix.  It was
defined in ASN.1 to provide a good storage format for the tables.

When snacc produces a type table it includes the useful types module
as well, if one was specified.  If you are really trying to limit the
size of your type tables, put the only useful types that you need in
your ASN.1 module and compile it without using the useful types
module.

A generic buffer type (à la ISODE and XDR) was defined to allow type
table driven routines to read from a wide variety of buffer formats.
Currently slightly modified versions of the {\C ExpBuf} and the
{\C SBuf} are provided.  It shouldn't be too hard for you to add
support for your own buffer formats.  The generic buffers,
{\C GenBuf}s are described in more detail in a following section.

The general procedure for using type tables is to:
\begin{enumerate}
  \item Use snacc to compile your ASN.1 modules into a type table.
  \item Use {\ufn mkchdr} (make C header, not make cheddar) with the
    type table to produce a friendly C type description of the types in
    the type table.
  \item Load the type table during runtime and use it to configure the table
    encode, decode and other routines.
\end{enumerate}

Step two, making the C header file is not necessary but will make
dealing with the value easier and more type safe.  Internally the
table driven encoders and decoders know nothing of these header file
and treat the types in a uniform, generic manner.  This requires the
encoders and decoders to make assumptions about the way C represents
strucutures and values.  Look in the {\ufn \dots/c-lib/src/tbl-enc.c} and
{\ufn \dots/c-lib/src/tbl-dec.c} files to see how this generic data
structure is manipulated.

On the down side, the compiler directives do not affect the data
structures generated by mkchdr and the generated C type definitions
will generally be different from those generated by the C backend.
This can be fixed, but time was lacking. Type tables also do not
support ANY DEFINED BY types.  Someone could fix this without too much
difficulty.  Only a C type table library is provided.  I didn't have
time to deal with the complexities of creating C++ objects in a
generic way.

Currently the type tables are lacking subtyping information.  It is
available in snacc's main parse tree but I didn't have time to add it
to the tables.  If you want to add it, take the subtype related data
structures (in ASN.1) from asn1module.asn1 (quite a few), remove all
the cruft pertaining to linking and error checking etc, and add it to
the type table type definitions.  Then change the
{\ufn \dots/compiler/core/gen-tbls.c} file to take the subtype information from
the parse tree and put it into the type table.  See the appendix or
{\ufn \dots/asn1specs/} for the ASN.1 definitions of the parse tree
and type tables.

The parse tree itself was defined in ASN.1 so it could be the table
format.  The extra complexity required for linking and error checking
made this very difficult.  Cycles in the data structure and the many
links between the data elements made encoding in BER difficult.
[Maybe ASN.1 needs a type refernce type (i.e. pointer)].


\section{How Type Table See Values}

As mentioned in the last section, table driven encoding, decoding,
printing etc. routines see your values in a generic way.  They do not
have abstract syntax specific header files like those created by
{\ufn mkchdr}.

The basic idea is that all of the standard primitive and list
(SEQUENCE OF and SET OF) types are used and some regular rules are
used for allocating and laying out structs for SEQUENCE, SET and
CHOICE types.

\begin{small}
\begin{verbatim}
/* values from table driver routines' point of view */
typedef void AVal;

/* SEQUENCE and SET type use AStructVal */
typedef AVal *AStructVal;  /* an array of AVal ptrs */
\end{verbatim}
\end{small}

For SETs and SEQUENCEs, the {\C AStructVal} type is used. Its is
basically an array of pointers, one for each component of the
SET/SEQUENCE\@.  Every component is referenced by pointer to simplify
allocations.  OPTIONAL or DEFAULT components may be NULL\@.  For
example, the type:

\begin{small}
\begin{verbatim}
Foo ::= SEQUENCE { a INTEGER, b BOOLEAN, c OCTET STRING }
\end{verbatim}
\end{small}

would be represented as an array of three pointers.  The first pointer
would point to an {\C AsnInt} value, the second would point to an
{\C AsnBool} value, and the third would point to an {\C AsnOcts}
value. {\ufn mkchdr} would give you the following C typedef for Foo:

\begin{small}
\begin{verbatim}
typedef struct Foo
{
    AsnInt *a;
    AsnBool *b;
    AsnOcts *c;
} Foo;
\end{verbatim}
\end{small}

\begin{small}
\begin{verbatim}
/* Internal representation for a CHOICE type */
typedef struct AChoiceVal
{
   enum { achoiceval_notused } choiceId;
   AVal *val;
} AChoiceVal;
\end{verbatim}
\end{small}


A CHOICE type is represented in a way similar to the C backend's
output.  That is, an enum to hold the id of the CHOICE component
that is present and a pointer to the component itself.  For example,
the type:

\begin{small}
\begin{verbatim}
Bar ::= CHOICE { a INTEGER, b BOOLEAN, c OCTET STRING }
\end{verbatim}
\end{small}

would internally be represented as AChoiceVal type.  However,
{\ufn mkchdr} would give you the following:

\begin{small}
\begin{verbatim}
typedef struct Bar
{
    enum
    {
        a_ID = 0,
        b_ID = 1,
        c_ID = 2
    } choiceId;
    union
    {
        AsnInt *a;
        AsnBool *b;
        AsnOcts *c;
    } a;
} Bar;
\end{verbatim}
\end{small}


\section{Type Table Utilities}

There are a bunch of useful routines in {\ufn \dots/c-lib/src/tbl*.c}.  Look
at the source code in {\ufn \dots/tbl-tools/*/} and
{\ufn \dots/tbl-example/} to see how to use some of them.

The {\C LoadTblFile} will decode a type table from a given file.
Notice that its definition of the {\C TBL} data structure has been
augmented to simplify encoding and decoding operations. (Look at the
patch in {\ufn \dots/c-lib/tbl.h.patch} that is applied through the makefile automatically.)
% The additional fields do not screw up the compiled encoder/decoder (in {\ufn \dots/c-lib/src/tbl.c}) provided they are recompiled.
The compiler uses unmodified {\ufn tbl.h} and {\ufn tbl.c} files.

I don't have time to document these routines.  Look through the table
tools, examples and library code.  Their usage should be fairly
obvious.


\section{Type Table Tools}

The {\ufn \dots/tbl-tools/} directory contains three tools, {\ufn mkchdr},
{\ufn pval} and {\ufn ptbl}.  These are described in the following
sections.

\subsection{Making C Header Files with mkchdr}

{\ufn mkchdr} produces a C header file from a type table.  This header
file shows the representation of the types that the table tools will
expect or return for the types in the given type table.

The main use is to provide you with an easy to use definition of the
ASN.1 types C representation.  You do not need to use {\ufn mkchdr}
but it is definately recommended.  Note that the table routines could
have used an even more generic data structure to represent values
(e.g. ISODE's Presentation Elements).  If you have worked with these,
you know that they are cumbersome.

Its synopsis is:
\begin{small}
\begin{verbatim}
mkchdr <tbl-file> [output-file]
\end{verbatim}
\end{small}

If the output file is omitted, the header file is printed to {\C stdout}.

Here is an example of the output.  Given the table that has the
following ASN.1 module in it:

\begin{small}
\begin{verbatim}
P-REC DEFINITIONS ::=
BEGIN
PersonnelRecord ::=  [APPLICATION 0] IMPLICIT SET
{
                     Name,
    title        [0] IA5String,
                     EmployeeNumber,
    dateOfHire   [1] Date,
    nameOfSpouse [2] Name,
    children     [3] IMPLICIT SEQUENCE OF ChildInformation DEFAULT {}
}

ChildInformation ::= SET
{
                    Name,
    dateOfBirth [0] Date
}

Name ::= [APPLICATION 1] IMPLICIT SEQUENCE
{
    givenName  IA5String,
    initial    IA5String,
    familyName IA5String
}

EmployeeNumber ::= [APPLICATION 2] IMPLICIT INTEGER

Date ::= [APPLICATION 3] IMPLICIT IA5String -- YYYYMMDD

END
\end{verbatim}
\end{small}

{\ufn mkchdr} will produce:
\begin{small}
\begin{verbatim}
typedef AsnInt EmployeeNumber;

typedef struct Name
{
    IA5String *givenName;
    IA5String *initial;
    IA5String *familyName;
} Name;

typedef IA5String Date;

typedef struct ChildInformation
{
    Name *field0;
    Date *dateOfBirth;
} ChildInformation;

typedef AsnList PersonnelRecordSeqOf;

typedef struct PersonnelRecord
{
    Name *field0;
    IA5String *title;
    EmployeeNumber *field1;
    Date *dateOfHire;
    Name *nameOfSpouse;
    PersonnelRecordSeqOf *children;
} PersonnelRecord;

\end{verbatim}
\end{small}


\subsection{Printing Tables with ptbl}

{\ufn ptbl} is a program that will show you the contents of a type
table.  It can print a table in two modes:
\begin{itemize}
  \item The value notation for the TBL ASN.1 data structure (see the appendix).
  \item The ASN.1 text version
\end{itemize}

Its synopsis is:

\begin{small}
\begin{verbatim}
ptbl [-a] <tbl-file>
\end{verbatim}
\end{small}

For example, using {\ufn ptbl -a p-rec.tt} to print the
PersonnelRecord module used in the last section would yield:

\begin{small}
\begin{verbatim}
P-REC  DEFINITIONS ::=
BEGIN
EmployeeNumber ::= [APPLICATION 2] IMPLICIT INTEGER
Name ::= [APPLICATION 1] IMPLICIT SEQUENCE
{
    givenName IA5String,
    initial IA5String,
    familyName IA5String
}
Date ::= [APPLICATION 3] IMPLICIT IA5String
ChildInformation ::= SET
{
    Name,
    dateOfBirth [0] Date
}
PersonnelRecordSeqOf ::= SEQUENCE OF ChildInformation
PersonnelRecord ::= [APPLICATION 0] IMPLICIT SET
{
    Name,
    title [0] IA5String,
    EmployeeNumber,
    dateOfHire [1] Date,
    nameOfSpouse [2] Name,
    children [3] IMPLICIT PersonnelRecordSeqOf
}
END
--  Definitions for  ASN-USEFUL
ASN-USEFUL  DEFINITIONS ::=
BEGIN
ObjectDescriptor ::= [UNIVERSAL 7] IMPLICIT OCTET STRING
NumericString ::= [UNIVERSAL 18] IMPLICIT OCTET STRING
PrintableString ::= [UNIVERSAL 19] IMPLICIT OCTET STRING
TeletexString ::= [UNIVERSAL 20] IMPLICIT OCTET STRING
T61String ::= [UNIVERSAL 20] IMPLICIT OCTET STRING
VideotexString ::= [UNIVERSAL 21] IMPLICIT OCTET STRING
IA5String ::= [UNIVERSAL 22] IMPLICIT OCTET STRING
GraphicString ::= [UNIVERSAL 25] IMPLICIT OCTET STRING
VisibleString ::= [UNIVERSAL 26] IMPLICIT OCTET STRING
ISO646String ::= [UNIVERSAL 26] IMPLICIT OCTET STRING
GeneralString ::= [UNIVERSAL 27] IMPLICIT OCTET STRING
UTCTime ::= [UNIVERSAL 23] IMPLICIT OCTET STRING
GeneralizedTime ::= [UNIVERSAL 24] IMPLICIT OCTET STRING
EXTERNAL ::= [UNIVERSAL 8] IMPLICIT SEQUENCE
{
    direct-reference OBJECT IDENTIFIER,
    indirect-reference INTEGER,
    data-value-descriptor ObjectDescriptor,
    encoding CHOICE
    {
        single-ASN1-type [0] OCTET STRING,
        octet-aligned [1] IMPLICIT OCTET STRING,
        arbitrary [2] IMPLICIT BIT STRING
    }
}
END
\end{verbatim}
\end{small}

Note that the useful type module is included in the table.  As
mentioned before, to minimize the size of your tables, put the
definitions of the useful types (from {\ufn \dots/asn1specs/asn-useful.asn1})
into your ASN.1 module and do not compile with useful types module
(i.e. don't use the -u option).  If your module doesn't use any
useful types, just don't use the -u option.

The other mode of {\ufn ptbl}, value notation for the TBL data
structure, produces a lot of output.  It may be useful if you are
debugging one of the table drivers.


\subsection{Printing Values with pval}

The {\ufn pval} program will convert BER values into their value
notation.  Its arguments are a type table file, a module and type name
and a list of BER files of that type.

Its synopsis is:

\begin{small}
\begin{verbatim}
pval -T <tt file name> [-m <module name>] -n <type name> <ber value file list>
\end{verbatim}
\end{small}

Running {\ufn pval} on a PersonnelRecord value produced the following:

\begin{small}
\begin{verbatim}
-- Contents of file "demo-tbls/p-rec.ber"--
-- module = ???, type = PersonnelRecord --

value P-REC.PersonnelRecord ::=
{
  {
    givenName '4a6f686e'H  -- "John" --,
    initial '45'H  -- "E" --,
    familyName '536d697468'H  -- "Smith" --
  },
  title '5468652042696720436865657365'H  -- "The Big Cheese" --,
  99999,
  dateOfHire '3139383230313034'H  -- "19820104" --,
  nameOfSpouse {
    givenName '4d617279'H  -- "Mary" --,
    initial '4c'H  -- "L" --,
    familyName '536d697468'H  -- "Smith" --
  },
  children {
    {
      {
        givenName '4a616d6573'H  -- "James" --,
        initial '52'H  -- "R" --,
        familyName '536d697468'H  -- "Smith" --
      },
      dateOfBirth '3139353730333130'H  -- "19570310" --
    },
    {
      {
        givenName '4c697361'H  -- "Lisa" --,
        initial '4d'H  -- "M" --,
        familyName '536d697468'H  -- "Smith" --
      },
      dateOfBirth '3139363130363231'H  -- "19610621" --
    }
  }
}

 -- decoded 143 bytes for the above value --
\end{verbatim}
\end{small}


\section{Using Tables in Your Own Applications}

The best way to get a handle on using tables is to look at the example
in {\ufn \dots/tbl-example/}.  The general idea is to compile your
ASN.1 into a type table (use the snacc {\ufn -T} option).  If you desire a
livable definition of the C data structures for the types in the type
table, run {\ufn mkchdr} and compile the generated header file with
your C code.  During runtime, simply load your table file with
{\C LoadTblFile} (I use the {\ufn .tt} suffix naming convention for type table
files but it doesn't matter) and then use the {\C TblEncode},
{\C TblDecode}, {\C TblPrint} and {\C TblFree} routines with
your table.  Quite simple.  Seriously.

\section{Using GenBufs}

The {\C GenBuf} generic buffers are really a way of encapsulating
other buffer formats.  A {\C GenBuf} contains a table of pointers to
the buffer functions (the standardized ones (see the buffer section)
plus ``Peek'' routines that the table drivers needed).  They are
defined in {\ufn \dots/c-lib/inc/gen-buf.h}.

{\C GenBuf}s require functions for the standard buffer routines,
macros will not do since you cannot have a pointer to macro.

The benefit of the {\C GenBuf}s is that since they can support other
buffer types, only one set of library routines is needed.  (Note that
there are 3 libraries in {\ufn \dots/c-lib/} for the backend model and only one for the type table model.

Here is most of {\ufn gen-buf.h} to give you an idea of how things work:

\begin{small}
\begin{verbatim}
typedef unsigned char (*BufGetByteFcn) PROTO ((void *b));
typedef unsigned char *(*BufGetSegFcn) PROTO ((void *b,
                                               unsigned long int *lenPtr));
typedef long int (*BufCopyFcn) PROTO ((char *dst, void *b,
                                   unsigned long int len));
typedef void (*BufSkipFcn) PROTO ((void *b, unsigned long int len));
typedef unsigned char (*BufPeekByteFcn) PROTO ((void *b));
typedef unsigned char *(*BufPeekSegFcn) PROTO ((void *b,
                                               unsigned long int lenPtr));
typedef long int (*BufPeekCopyFcn) PROTO ((char *dst, void *b,
                                   unsigned long int len));
typedef void (*BufPutByteRvsFcn) PROTO ((void *b, unsigned char byte));
typedef void (*BufPutSegRvsFcn) PROTO ((void *b, char *data,
                                    unsigned long int len));
typedef int (*BufReadErrorFcn) PROTO ((void *b));
typedef int (*BufWriteErrorFcn) PROTO ((void *b));

typedef struct GenBuf
{
    BufGetByteFcn    getByte;
    BufGetSegFcn     getSeg;
    BufCopyFcn       copy;
    BufSkipFcn       skip;
    BufPeekByteFcn   peekByte;
    BufPeekSegFcn    peekSeg;
    BufPeekCopyFcn   peekCopy;
    BufPutByteRvsFcn putByteRvs;
    BufPutSegRvsFcn  putSegRvs;
    BufReadErrorFcn  readError;
    BufWriteErrorFcn writeError;
    void             *bufInfo;
    void             *spare; /* hack to save space for ExpBuf ** type */
} GenBuf;

#define GenBufGetByte( b)              ((b)->getByte (b->bufInfo))
#define GenBufGetSeg( b, lenPtr)       ((b)->getSeg (b->bufInfo, lenPtr))
#define GenBufCopy( dst, b, len)       ((b)->copy (dst, b->bufInfo, len))
#define GenBufSkip( b, len)            ((b)->skip (b->bufInfo,len))
#define GenBufPeekByte( b)             ((b)->peekByte (b->bufInfo))
#define GenBufPeekSeg( b, lenPtr)      ((b)->peekSeg (b->bufInfo, lenPtr))
#define GenBufPeekCopy( dst, b, len)   ((b)->peekCopy (dst, b->bufInfo, len))
#define GenBufPutByteRvs( b, byte)     ((b)->putByteRvs (b->bufInfo, byte))
#define GenBufPutSegRvs( b, data, len) ((b)->putSegRvs (b->bufInfo, data, len))
#define GenBufReadError( b)            ((b)->readError (b->bufInfo))
#define GenBufWriteError( b)           ((b)->writeError (b->bufInfo))
\end{verbatim}
\end{small}

\section{Type Tables Vs. Metacode}

Please refer to section~\ref{meta-ttab-comparison} on page~\pageref{meta-ttab-comparison}.