/*******************************************************************
 *
 * This file was generated by TIS/ASN1COMP Ver. 4.2, an ASN.1 compiler.
 * TIS/ASN1COMP is Copyright (c) 1998, TIS Labs at Network Associates, Inc.
 *
 * This file was AUTOMATICALLY GENERATED on Tue May 18 17:09:44 1999
 *
 ******************************************************************/

/**********************************************************************
* pgpX509Cert_util.c
*
*  Utility routines for ASN.1 coding
************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "pgpX509Cert_util.h"

#ifdef PGPASN_TRACE
int PGPASN_TRACE_LEVEL = 0;
#endif

/***********************************************************************
* PGPASN_ErrorHalt
*
*  a dummy routine, called if error, on which one can halt/
*************************************************************************/

void PGPASN_ErrorHalt(int x)
{
    (void)x; /* not usted */
    return;
} /* err_halt */

/**********************************************************************
* Default mem. mgmt callbacks
*
* These are defaults that the user can use if desired, or use them
* as models for your own set of memory mgmt. callbacks.
***********************************************************************/

/* default memory allocator */
static void *
PGPASN_DefaultMemoryAllocationProc(
        PGPASN_MemoryMgr *mgr,
        size_t requestSize)
{
    (void)mgr;

    return ( malloc(requestSize) );

} /* PGPASN_DefaultMemoryAllocationProc */

/* default memory reallocator */
static int
PGPASN_DefaultMemoryReallocationProc(
        PGPASN_MemoryMgr *mgr,
        void **allocation,
        size_t newAllocationSize)
{
    int err;
    void *newPtr = NULL;
    void *oldPtr = *allocation;

    (void)mgr;

    newPtr = realloc(oldPtr, newAllocationSize);
    if (newPtr == NULL) {
        err = kPGPASNError_ErrOutOfMemory;
    }
    else {
        err = 0;
        *allocation = newPtr;
    }

    return err;

} /* PGPASN_MemoryMgrReallocationProc */

/* default memory deallocation routine */
static int
PGPASN_DefaultMemoryDeallocationProc(
        PGPASN_MemoryMgr *mgr,
        void *allocation)
{
    (void)mgr;

    free(allocation);

    return 0;

} /* PGPASN_MemoryMgrDeallocationProc */

/* the default PGPASN_MemoryMgr structure for the user */
PGPASN_MemoryMgr PGPASN_defaultMemoryMgrStruct = {
        NULL,
        PGPASN_DefaultMemoryAllocationProc,
        PGPASN_DefaultMemoryReallocationProc,
        PGPASN_DefaultMemoryDeallocationProc
};

/***************************************************************************
*  PGPASN_CompareElems
*  A routine to provide to the qsort routine for comparing two
*  <prefix>PrimVariableBlock blocks.  This is used by the pack
*  routines for SET OF structures.  This assumes the compare should
*  use the length of the longer element provided and for SET OF, then*  provided elements should have 0's at the ends of shorter elements.
****************************************************************************/

int PGPASN_CompareElems(const void *a, const void *b)
{
    int length;
    PGPASN_VariableBlock *locala = (PGPASN_VariableBlock *)a;
    PGPASN_VariableBlock *localb = (PGPASN_VariableBlock *)b;

    if (locala->len > localb->len)
        length = locala->len;
    else
        length = localb->len;

    return(memcmp(locala->val, localb->val, length));
} /* PGPASN_CompareElems */

/************************************************************************
*  PGPASN_LengthSize -- for scanning the length of a basic integer
*   for the ASN.1 length field after the tag.
*************************************************************************/

size_t PGPASN_LengthSize(size_t x)
{
  if ( x <= 127 )            return 1;
  else if ( x < 0x100 )     return 2;
  else if ( x < 0x10000 )   return 3;
  else if ( x < 0x1000000 ) return 4;
  else                      return 5;
} /* LengthSize */

/************************************************************************
* Tagged -- compute and return the size of a tagged ASN.1 object
*  whose size without the tag would be (inner).
*************************************************************************/

size_t PGPASN_Tagged(size_t inner, int seqlike )
{
  if ( inner == 0 && ! seqlike )
      return (0);
  return ( 1 + inner + PGPASN_LengthSize(inner) );
} /* Tagged */

/************************************************************************
* PGPASN_PutByte -- put an ASN.1 type byte to op
*************************************************************************/

size_t PGPASN_PutByte( unsigned char *buf, unsigned char byte )
{
    *buf = byte;
    return 1;

} /* PGPASN_PutByte */

/************************************************************************
* PGPASN_PutLength -- put a length field to opp
************************************************************************/

size_t PGPASN_PutLength( unsigned char *buf, size_t length )
{
    unsigned char *bufptr = buf;
    size_t size = PGPASN_LengthSize(length) - 1;
    unsigned char bytes[4] ;
    unsigned char *bp = &(bytes[3]) ; /* pointer for unpacking */

    switch (size) {
      case 4: *(bp--) = (unsigned char)length & 0xff ; length >>= 8 ;
      case 3: *(bp--) = (unsigned char)length & 0xff ; length >>= 8 ;
      case 2: *(bp--) = (unsigned char)length & 0xff ; length >>= 8 ;
      case 1: *(bp--) = (unsigned char)length & 0xff ; length >>= 8 ;
      default: /* fall through */ ;
    } /* turn x into bytes */

    /* only need one byte for length */
    if (size == 0)
        *(bufptr++) = (unsigned char)(length & 0x7f);
    /* otherwise first byte is number of bytes needed for size */
    else
        *(bufptr++) = (unsigned char)(0x80 | size) ;

    size = 4 - size; /* as an index into bytes[] */
    while (size < 4) *(bufptr++) = bytes[size++] ;

    /* return the number of bytes used */
    return (size_t)(bufptr-buf);

} /* PGPASN_PutLength */

/************************************************************************
* PGPASN_GetByte -- get an ASN.1 type byte to op
*************************************************************************/

size_t PGPASN_GetByte(const unsigned char *buf, unsigned char *byte)
{
    *byte = *buf;
    return 1;

} /* PGPASN_GetByte */

/************************************************************************
* PGPASN_GetLength -- fetch a length field from opp and return it
*  If this is indefinite length, then return -1 for the length.
*************************************************************************/

size_t PGPASN_GetLength(const unsigned char *buf, size_t *length)
{
    size_t bytesused;
    unsigned long x ;
    unsigned char c ;

    c = *buf; /* get the control byte (or length) */
    bytesused = 1;

    if (c == 0x80) { /* this is indefinite length */
       *length = -1;
       return bytesused;
    }

    if ((c & 0x80) == 0) {     /* have the length already */
        *length = (size_t)c;   /* note the byte we used */
        PGPASN_TRACE_PRINT_LENGTH(c);
        return bytesused; /* return the length */
    } /* if */

    c &= 0x7f ; /* get the number of bytes of length */
    x = 0 ;     /* clear x */
    while ((c--) > 0) {
        x <<= 8 ; /* shift up the word */
        x |= (unsigned long)*(buf + bytesused); /* gather the next byte */
        bytesused++;
    } /* pack as integer */

    PGPASN_TRACE_PRINT_LENGTH((int)x);

    *length = (size_t)x; /* note the bytes used */
    return bytesused;    /* return the length */

} /* PGPASN_GetLength */

/************************************************************************
* PGPASN_PutTag
*
*  Put a tag field (tag + length) to the output block
*************************************************************************/

size_t PGPASN_PutTag( unsigned char *buf, unsigned char tagbyte, size_t length )
{
    size_t bytesused;

    bytesused = PGPASN_PutByte(buf, tagbyte);
    bytesused += PGPASN_PutLength(buf+bytesused, length);

    return bytesused;

} /* PGPASN_PutTag */

/************************************************************************
* PGPASN_TakeTag
*
*  Get a tag field from the input block and return its length
*************************************************************************/

size_t PGPASN_TakeTag(const unsigned char *buf, unsigned char tag, size_t *length)
{
    size_t bytesused = 0;

    *length = 0;

    if (buf == NULL) return 0;
    if (*buf != tag) return 0;

    /* have that tag */
    bytesused = 1; /* consume the tag byte */
    bytesused += PGPASN_GetLength(buf+bytesused, length);

    return bytesused;
} /* PGPASN_TakeTag */

/************************************************************************
* Basic variable block routines
*************************************************************************/

void *pgpasn_NewVariableBlock(PGPASN_CONTEXT *ctx)
{
    PGPASN_VariableBlock *block = NULL ;

    if (ctx == NULL)
        return NULL;

    block = (PGPASN_VariableBlock *) PGPASN_Alloc(ctx->memMgr,
                                       sizeof(PGPASN_VariableBlock) );

    if (block != NULL)
        memset(block, 0, sizeof(PGPASN_VariableBlock)) ;
    return (void *)block;

} /* pgpasn_NewVariableBlock */

size_t pgpasn_SizeofVariableBlock(PGPASN_CONTEXT *ctx,
                     PGPASN_VariableBlock *block,
                     int outerSizeFlag)
{
    (void)ctx; /* for future use */

    return pgpasn_SizeofVariableBlockInternal(block, outerSizeFlag, PGPASN_FALSE);
}

/*****
 *
 * internal pgpasn_SizeofVariableBlock
 *
 * This internal routine handles the case where there is an
 * explicitly tagged field in a constructed ASN block.  The final length
 * returned will be increased by the explicit tag bits (using the tagged()
 * routine) only if the previous calculations don't result in zero.
 *
 * It is assumed that outerSizeFlag will not be FALSE when expTaggedSize
 * is TRUE.  This is handled by the generated code.  It does not call 
 * _sizeof with this case.  Also, the wrapper routine above calls this
 * routine with expTaggedSize as FALSE.  So, this routine doesn't do any
 * error checking in that respect.
 *
 *****/
size_t pgpasn_SizeofVariableBlockInternal(
    PGPASN_VariableBlock *block,
    int outerSizeFlag,
    int expTaggedSize)
{
    size_t length;

    if (block == NULL)
              return (0);

    length = (size_t)block->len;

    if (outerSizeFlag == PGPASN_TRUE)
        length = length + 1 + PGPASN_LengthSize(block->len);

    if (expTaggedSize == PGPASN_TRUE)
        length = PGPASN_Tagged(length, 0);

    return length;

} /* pgpasn_SizeofVariableBlockInternal */

void pgpasn_DropInPlaceVariableBlock(PGPASN_CONTEXT *ctx,
                PGPASN_VariableBlock *f )
{
    if (ctx == NULL)
        return;

    if (f != NULL && f->val != NULL) {
        PGPASN_Free(ctx->memMgr, f->val);
        f->val = NULL;
    }
} /* pgpasn_DropInPlaceVariableBlock */

void pgpasn_FreeVariableBlock(PGPASN_CONTEXT *ctx,
                 PGPASN_VariableBlock *f)
{
    if (ctx == NULL)
        return;

    pgpasn_DropInPlaceVariableBlock(ctx, f);
    if (f != NULL)
        PGPASN_Free(ctx->memMgr, f);

    return;
} /* pgpasn_FreeVariableBlock */

/************************************************************************
* pack and unpack -- taking block type as a parameter/
*************************************************************************/

static size_t UnpkInPlaceSegments(
    PGPASN_CONTEXT *ctx,
    int indefFlag,               /* is this an indefinite length block or not? */
    unsigned char exptag,      /* my expected block tag */
    PGPASN_VariableBlock *asnblock, /* output block */
    const unsigned char *buf,  /* loc of input pointer */
    size_t buflen,             /* max end of my region */
    int *erret)                /* error return location */
{
    size_t bytesused = 0;
    size_t datasize;

    /* validity of ctx and erret checked in UnpkInPlaceVariableBlock */

    if (buf == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoBlockPtr);
        return 0;
    }

    /* validity of asnblock checked in UnpkInPlaceVariableBlock */
    asnblock->val = NULL;
    asnblock->len = 0;

    /* while there are segments */
    while (1) {

        /* For indef length end-of-contents is 0x00 0x00 and we
           have to eat those two bytes, otherwise we see if we've
           used up all the available data yet */
        if ( indefFlag == 1 &&
            (*(buf+bytesused) == 0x00 && *(buf+bytesused+1) == 0x00) ) {
            bytesused += 2;
            break;
        }
        else if (indefFlag == 0 && bytesused == buflen)
            break;

        if (*buf != exptag) {
            PGPASN_ERR(kPGPASNError_ErrUnpackInvalidEncoding);
            return 0;
        }
        bytesused++; /* skip the tag byte */

        datasize = 0;
        bytesused += PGPASN_GetLength(buf+bytesused, &datasize);

        /* no nested indef lengths for string types */
        if ((int)datasize == -1) { 
            PGPASN_ERR(kPGPASNError_ErrUnpackInvalidEncoding);
            return 0;
        }

        if (bytesused + datasize > buflen) {
            PGPASN_ERR(kPGPASNError_ErrUnpackOverrun); /* note this error */
            return 0;
        }

        if (datasize == 0)
            continue;

        PGPASN_Realloc(ctx->memMgr,
                  (void **)&asnblock->val, asnblock->len+datasize);
        if (asnblock->val == NULL) {
            PGPASN_ERR(kPGPASNError_ErrOutOfMemory);
            return 0;
        }

        memcpy(asnblock->val + asnblock->len,
               buf+bytesused, datasize);
        asnblock->len += datasize;
        bytesused += datasize;

    } /* while there are segments */

    return bytesused;
} /* UnpkInPlaceSegments */

static size_t UnpkInPlaceVariableBlock(
    PGPASN_CONTEXT *ctx,
    unsigned char exptag, /* my expected block tag */
    const char *name,           /* my block name */
    unsigned char tag,    /* my real tag */
    PGPASN_VariableBlock *asnblock, /* output block */
    const unsigned char *buf,   /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    int *erret)           /* error return location */
{
    size_t bytesused;
    size_t datasize;
    int constructed = 0;
    int indef = 0;

    PGPASN_TRACE_name(name);
    PGPASN_TRACE_PRINT_FM(exptag, tag, name);

    if (erret == NULL)
        return 0; /* can't report errors */

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (asnblock == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    if (buf == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoBlockPtr);
        return 0;
    }
    if (buflen <= 0)
        return 0; /* no error -- no block */

    /* we need to ignore the 6th bit for this check, if this is
       BER then it may be set, the checks below
       test for those cases */
    if ( (*buf & 0xDF) != (exptag & 0xDF) ) 
        return 0; /* no error code, just no block */
    if ( (*buf & 0x20) == 0x20)
        constructed = 1;

    bytesused = 1;

    bytesused += PGPASN_GetLength(buf+bytesused, &datasize);

    if ((int)datasize == -1) { /* indef length */
        datasize = 0;
        indef = 1;
    }

    if (bytesused+datasize > buflen) {
        PGPASN_ERR(kPGPASNError_ErrUnpackOverrun); /* note this error */
        return 0;
    }

    if (indef == 1 && constructed != 1) {
          PGPASN_ERR(kPGPASNError_ErrUnpackInvalidEncoding);
          return 0;
    }

    if (asnblock->val != NULL)
        PGPASN_Free(ctx->memMgr, asnblock->val); /* about to carve it */
  
    PGPASN_TRACE_PRINT_DATA(buf+bytesused,datasize);

    if (constructed == 0) { /* primitive */
          asnblock->len = datasize; /* record the length */
          asnblock->val = (unsigned char *)PGPASN_Alloc(ctx->memMgr, datasize);
        if (asnblock->val == NULL) {
            PGPASN_ERR(kPGPASNError_ErrOutOfMemory);
            return 0;
        }
          memcpy(asnblock->val, buf+bytesused, datasize);
          bytesused += datasize;
    }

    else if (indef == 1) { /* constructed, indefinite length */
        bytesused += UnpkInPlaceSegments(ctx, indef, tag, asnblock,
                                  buf+bytesused,
                                  buflen-bytesused, erret);
    }

    else { /* constructed, definite length */
        bytesused += UnpkInPlaceSegments(ctx, indef, 
                   (unsigned char)(exptag & 0xDF), asnblock,
                             buf+bytesused,
                             datasize, erret);
    }

   return bytesused;
} /* UnpkInPlaceVariableBlock */

/************************************************************************
* PackVariableBlock
*
*  Pack up an PGPASN_VariableBlock -- which could be an INTEGER, BIT STRING or
*  OCTET STRING.
*
*  Those three differ in padding (or its inverse).
*
*  For OCTET STRING, pad = 0
*  For BIT STRING, pad = 1, padb = # unused bits in the LSByte
*  For INTEGER, pad = 0..-N, where N is the # of unnecesary leading
*      0x00 or 0xff bytes
*
*   pad and padb have to be filled in by the caller.
*************************************************************************/

static size_t PackVariableBlock(
    PGPASN_CONTEXT *ctx,
    unsigned char blockType, /* my block type */
    int pad,                /* padding? (boolean) */
    unsigned char padbyte,  /* pad byte if (pad) */
    unsigned char *buf,     /* the output pointer pointer */
    size_t buflen,
    PGPASN_VariableBlock *block, /* the block I'm packing */
    int *erret)             /* an error */
{
    size_t bytesused = 0;
    unsigned char *lastbyte;
    unsigned char mask = 0xFF;

    (void)ctx; /* for future use */

    if (block == NULL) return 0;

    if (buf == NULL) {
        PGPASN_ERR(kPGPASNError_ErrPackNoBlock);
        return 0;
    }

    /* need room for tag + length bytes*/
    if (buflen < (1 + PGPASN_LengthSize(block->len+pad)) ) {
        PGPASN_ERR(kPGPASNError_ErrPackOverrun);
        return 0;
    }
    bytesused = PGPASN_PutTag(buf, blockType, block->len+pad);

    if (pad) {

        /* error here if padbyte > 7??? */

        if (buflen < (bytesused + 1 + block->len) ) {
            PGPASN_ERR(kPGPASNError_ErrPackOverrun);
            return 0;
        }
        /* add the pad byte before the data */
        bytesused += PGPASN_PutByte( buf+bytesused, padbyte);
        memcpy(buf+bytesused, block->val, block->len);
        bytesused += block->len;

        /* for DER bit strings, the padding bits must be zeros */
          if (padbyte != 0) {
              lastbyte = buf + bytesused - 1;
              mask = mask << padbyte; /* zeros are shifted in */
              *lastbyte = *lastbyte & mask;
        }
    }

    else {
        if (buflen < (bytesused + block->len) ) {
            PGPASN_ERR(kPGPASNError_ErrPackOverrun);
              return 0;
        }
          /* just add the data */
          memcpy(buf+bytesused, block->val, block->len);
          bytesused += block->len;
    }

    return bytesused;

} /* PackVariableBlock */

/************************************************************************
* pgpasn_PutOctVal
*
*  Put a octet buffer value away in a vbl block.
*************************************************************************/

int pgpasn_PutOctVal(
    PGPASN_CONTEXT *ctx,
    PGPASN_VariableBlock *block, /* the block for the value */
    const unsigned char *value, /* the value */
    size_t lth) /* the length */
{
    if (ctx == NULL)
        return kPGPASNError_ErrBadContext;
    if (block == NULL)
        return (-1);

    if (block->val != NULL)
        PGPASN_Free(ctx->memMgr, block->val);
    memset (block, 0, sizeof(PGPASN_VariableBlock));

    block->val = (unsigned char *)PGPASN_Alloc(ctx->memMgr, lth);
    if (block->val == NULL)
        return (-1);
    memcpy( block->val, value, lth ) ;
    block->len = lth ;
    return (0);
} /* pgpasn_PutOctVal */

/************************************************************************
* pgpasn_PutStrVal
*
*  Put a string value away in a vbl block.
*************************************************************************/

int pgpasn_PutStrVal(
    PGPASN_CONTEXT *ctx,
    PGPASN_VariableBlock *block,        /* the block for the value */
    const char *value)    /* the value */                 
{
    if (ctx == NULL)
        return kPGPASNError_ErrBadContext;

    if (block == NULL) return (-1);

    if (block->val != NULL)
        PGPASN_Free(ctx->memMgr, block->val);
    memset(block, 0, sizeof(PGPASN_VariableBlock));

    block->len = strlen(value);
    block->val = (unsigned char *)PGPASN_Alloc(ctx->memMgr, block->len);
    if (block->val == NULL)
        return (-1);
    memcpy(block->val, value, block->len);

    return (0);
} /* pgpasn_PutStrVal */

/************************************************************************
*pgpasn_GetStrVal
*
*  Return a string value found in a vbl block.
*************************************************************************/

char *pgpasn_GetStrVal(
    PGPASN_CONTEXT *ctx,
    PGPASN_VariableBlock *block)
{
    char *string;

    if (ctx == NULL)
        return NULL;
    if (block == NULL)
        return NULL;

    if (block->val == NULL || block->len == 0)
        return NULL;

    /* '+1' for NULL char */
    string = (char *)PGPASN_Alloc(ctx->memMgr, block->len + 1);
    if (string == NULL)
        return NULL;

    memcpy(string, block->val, block->len);
    string[block->len] = '\0';
 
    return string;

} /* pgpasn_GetStrVal */

/************************************************************************
* pgpasn_PutBitString
*
*  Put a bit string value away in a bitstring block.
************************************************************************/

int pgpasn_PutBitString (
    PGPASN_CONTEXT *ctx,
    PGPASN_BitstringBlock *block,  /* the block for the value */
    const unsigned char *string,   /* the value */
    size_t lth,                   /* the length */
    int nuub)            /* number of unused bits LSB */
{
    if (ctx == NULL)
        return kPGPASNError_ErrBadContext;
    if (block == NULL)
        return (-1);

    if ((block->val) != NULL)
        PGPASN_Free(ctx->memMgr, block->val);
    memset (block, 0, sizeof(PGPASN_BitstringBlock));
    block->val = (unsigned char *)PGPASN_Alloc(ctx->memMgr, lth);
    if (block->val == NULL)
        return (-1);
    memcpy(block->val, string, lth);
    block->len = lth;
    block->nuub = nuub;
    return 0;
}

/************************************************************************
* pgpasn_PutBoolVal
*
*  Put a boolean value in an PGPASN_BOOLEAN block.
*************************************************************************/
int pgpasn_PutBoolVal(
    PGPASN_CONTEXT *ctx,
    PGPASN_BOOLEAN *block,    /* the block for the value */
    int val)        /*  */
{
    (void)ctx; /* for future use */

    if (block == NULL)
        return (-1);

    if (val != 0) block->val = PGPASN_TRUE;
    else block->val = PGPASN_FALSE;
    block->len = 1;

    return 0;
}

/************************************************************************
* pgpasn_GetBoolVal
*
*  Put a boolean value in an PGPASN_BOOLEAN block.
*************************************************************************/
int pgpasn_GetBoolVal(
    PGPASN_CONTEXT *ctx,
    PGPASN_BOOLEAN *block)    /* the block for the value */
{
    (void)ctx; /* for future use */

    if (block == NULL) return (-1);

    if (block->val != 0) return PGPASN_TRUE;
    else return PGPASN_FALSE;
}

/************************************************************************
* NULL routines
*************************************************************************/

PGPASN_NULL *pgpasn_NewNULL(
    PGPASN_CONTEXT *ctx)
{
    PGPASN_NULL *f = NULL;

    if (ctx == NULL)
        return NULL;

    f = (PGPASN_NULL *)PGPASN_Alloc(ctx->memMgr, sizeof(PGPASN_NULL));
    if (f != NULL)
        memset(f, 0, sizeof(PGPASN_NULL) );

    return ((PGPASN_NULL *) f);
} /* pgpasn_NewNULL */

void pgpasn_DropInPlaceNULL(
    PGPASN_CONTEXT *ctx,
    PGPASN_NULL *f)
{
    (void)ctx;
    (void)f;
     return ;
} /* pgpasn_DropInPlaceNULL */

void pgpasn_FreeNULL(
    PGPASN_CONTEXT *ctx,
    PGPASN_NULL *g)
{
    if (ctx == NULL)
        return;
    if (g != NULL)
        PGPASN_Free(ctx->memMgr, g);
} /* pgpasn_FreeNULL */

size_t pgpasn_SizeofNULL(
    PGPASN_CONTEXT *ctx,
    PGPASN_NULL *b,
    int outerSizeFlag)
{
    (void)ctx; /* for future use */

    return pgpasn_SizeofNULLInternal(b, outerSizeFlag, PGPASN_FALSE);
} /* pgpasn_SizeofNULL */

size_t pgpasn_SizeofNULLInternal(
    PGPASN_NULL *b,
    int outerSizeFlag,
    int expTaggedFlag)
{
    size_t length = 0;
  
    if (b == NULL)
          return 0;

    if (outerSizeFlag == PGPASN_TRUE)
          length = 2;

    if (expTaggedFlag == PGPASN_TRUE)
          length = PGPASN_Tagged(length, 0);

    return length;

} /* pgpasn_SizeofNULLInternal */

size_t pgpasn_PackNULL(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_NULL *asnblock,
    int *erret)
{
    return (pgpasn_PackNULLInternal(ctx, buf, buflen, asnblock, PGPASN_ID_NULL, erret));
}

size_t pgpasn_PackNULLInternal(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_NULL *asnblock,
    unsigned char tag,
    int *erret)
{
    size_t bytesused;

    (void)ctx; /* for future use */

    if (erret == NULL) return 0;
    if (asnblock == NULL) return 0;

    bytesused = PGPASN_PutByte(buf, tag);
    bytesused += PGPASN_PutByte(buf+bytesused, 0x00); /* length = 0 bytes */

    if (bytesused > buflen) {
    PGPASN_ERR(kPGPASNError_ErrPackOverrun); /* note this error */
    return 0;
    }
    
    return bytesused;

} /* pgpasn_PackNULLInternal */

size_t pgpasn_UnpkInPlaceNULL(
    PGPASN_CONTEXT *ctx,
    PGPASN_NULL *nullstruct,    /* output block */
    const unsigned char *buf,  /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    unsigned char tag,        /* tag to use */
    int *erret             /* error return location */ )
{
    size_t bytesused;
    size_t datasize;

    PGPASN_TRACE_PRINT_FM(tag, 0x05, "NULL");

    (void)ctx; 

    if (erret == NULL) return 0;    /* can't report errors */
    if (nullstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    if (buf == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoBlockPtr);
        return 0;
    }
    if (buflen <= 0) return 0; /* no error -- no block */

    if (*buf != tag)
        return 0;    /* no error, no block */
    bytesused = 1;

    bytesused += PGPASN_GetLength(buf+bytesused, &datasize);
    if (datasize != 0) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNullLth);
        return 0;
    }

    if (bytesused > buflen) {
        PGPASN_ERR(kPGPASNError_ErrUnpackOverrun); /* note this error */
        return 0;
    }

    nullstruct->len = 0;
    return bytesused;
} /* pgpasn_UnpkInPlaceNULL */

size_t pgpasn_UnpackNULL(
    PGPASN_CONTEXT *ctx,
    PGPASN_NULL **nullstruct,
    const unsigned char *buf,
    size_t buflen, 
    int        *erret)  /* error return */
{
    return(pgpasn_UnpackNULLInternal(ctx, nullstruct, buf, buflen, PGPASN_ID_NULL, erret));
} /* pgpasn_UnpackNULL */

size_t pgpasn_UnpackNULLInternal(
    PGPASN_CONTEXT *ctx,
    PGPASN_NULL **nullstruct,
    const unsigned char *buf,
    size_t buflen,
    unsigned char tag,
    int        *erret)  /* error return */
{
    size_t bytesused;
    PGPASN_NULL *local = NULL;

    if (erret == NULL)
        return 0;
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (nullstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    *nullstruct = NULL;

    if (buflen <= 0) return 0;

    if (*buf != tag) return 0;

    local = pgpasn_NewNULL(ctx);        /* carve a block for it */
    bytesused = pgpasn_UnpkInPlaceNULL(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) pgpasn_FreeNULL(ctx, local);
        return 0;
    }

    *nullstruct = local;
    return bytesused;
} /* pgpasn_UnpackNULLInternal */

/************************************************************************
* PGPASN_BOOLEAN routines
*************************************************************************/

PGPASN_BOOLEAN *pgpasn_NewBOOLEAN(
    PGPASN_CONTEXT *ctx)
{
    PGPASN_BOOLEAN *f = NULL;

    if (ctx == NULL)
        return NULL;

    f = (PGPASN_BOOLEAN *)PGPASN_Alloc(ctx->memMgr, sizeof(PGPASN_BOOLEAN) );
    if (f != NULL)
        memset(f, 0, sizeof(PGPASN_BOOLEAN) );

    return ((PGPASN_BOOLEAN *) f);
} /* NewBOOLEAN */

void pgpasn_DropInPlaceBOOLEAN(
    PGPASN_CONTEXT *ctx,
    PGPASN_BOOLEAN *f)
{
    (void)ctx;
    (void)f;
    return ;
} /* pgpasn_DropInPlaceBOOLEAN */

void pgpasn_FreeBOOLEAN(
    PGPASN_CONTEXT *ctx,
    PGPASN_BOOLEAN *g )
{
    if (ctx == NULL)
        return;
    if (g != NULL)
        PGPASN_Free(ctx->memMgr, g);
} /* pgpasn_FreeBOOLEAN */

size_t pgpasn_SizeofBOOLEAN(
    PGPASN_CONTEXT *ctx,
    PGPASN_BOOLEAN *boolblock,
    int outerSizeFlag)
{
    (void)ctx; /* for future use */

    return pgpasn_SizeofBOOLEANInternal(boolblock, outerSizeFlag, PGPASN_FALSE);
}

size_t pgpasn_SizeofBOOLEANInternal(
    PGPASN_BOOLEAN *boolblock,
    int outerSizeFlag,
    int expTaggedSize)
{
    size_t length = 1;

    if (boolblock == NULL)
        return 0;

    if (outerSizeFlag == PGPASN_TRUE)
        length = 3;

    if (expTaggedSize == PGPASN_TRUE)
        length = PGPASN_Tagged(length, 0);

    return length;

} /* pgpasn_SizeofBOOLEANInternal */

size_t pgpasn_PackBOOLEAN(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_BOOLEAN *boolblock,
    int *erret )
{
    return(pgpasn_PackBOOLEANInternal(ctx, buf, buflen, boolblock,  PGPASN_ID_BOOLEAN, erret));
} /* pgpasn_PackBOOLEAN */

size_t pgpasn_PackBOOLEANInternal(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_BOOLEAN *boolblock,
    unsigned char tag,
    int *erret )
{
    unsigned char value = 0x00;
    size_t bytesused;

    (void)ctx; /* for future use */

    if (boolblock == NULL) return 0;

    if (boolblock->val != 0) value = 0xff;

    bytesused = PGPASN_PutByte(buf, tag);    /* this is a PGPASN_BOOLEAN */
    bytesused += PGPASN_PutByte(buf+bytesused, 1);    /* length = 1 byte */
    bytesused += PGPASN_PutByte(buf+bytesused, value); /* the value */

    if (bytesused > buflen) {
        PGPASN_ERR(kPGPASNError_ErrPackOverrun); /* note this error */
        return 0;
    }

    return bytesused;
} /* pgpasn_PackBOOLEANInternal */

size_t pgpasn_UnpkInPlaceBOOLEAN(
    PGPASN_CONTEXT *ctx,
    PGPASN_BOOLEAN *boolstruct,    /* output block */
    const unsigned char *buf,        /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    unsigned char tag,
    int *erret)            /* error return location */
{
    size_t bytesused;
    size_t datasize;

    (void)ctx; 

    PGPASN_TRACE_PRINT_FM(tag, 0x01, "BOOLEAN");

    if (erret == NULL) return 0;    /* can't report errors */
    if (boolstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    if (buf == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoBlockPtr);
        return 0;
    }
    if (buflen <= 0) return 0; /* no error -- no block */

    if (*buf != tag) return 0;
    bytesused = 1;

    bytesused += PGPASN_GetLength(buf+bytesused, &datasize);
    if (datasize != 1) {
        PGPASN_ERR( kPGPASNError_ErrUnpackBooleanLth);
        return 0;
    }

    if (bytesused+datasize > buflen) {
        PGPASN_ERR(kPGPASNError_ErrUnpackOverrun); /* note this error */
        return 0;
    }

    PGPASN_TRACE_PRINT_DATA(buf+bytesused,1);

    bytesused += PGPASN_GetByte(buf+bytesused, (unsigned char *)&(boolstruct->val));
    if (boolstruct->val != 0) boolstruct->val = PGPASN_TRUE;

    return bytesused;
} /* pgpasn_UnpkInPlaceBOOLEAN */

size_t pgpasn_UnpackBOOLEAN(
    PGPASN_CONTEXT *ctx,
    PGPASN_BOOLEAN **boolstruct,
    const unsigned char *buf,
    size_t buflen,
    int        *erret)  /* error return */
{
    return(pgpasn_UnpackBOOLEANInternal(ctx, boolstruct, buf, buflen,
                               PGPASN_ID_BOOLEAN, erret));
}

size_t pgpasn_UnpackBOOLEANInternal(
    PGPASN_CONTEXT *ctx,
    PGPASN_BOOLEAN **boolstruct,
    const unsigned char *buf,
    size_t buflen,
    unsigned char tag,
    int        *erret)  /* error return */
{
    size_t bytesused;
    PGPASN_BOOLEAN *local = NULL;

    if (erret == NULL)
        return 0;
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (boolstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    *boolstruct = NULL;

    if (buflen <= 0) return 0;

    if (*buf != tag) return 0;

    local = pgpasn_NewBOOLEAN(ctx);    /* carve a block for it */
    bytesused = pgpasn_UnpkInPlaceBOOLEAN(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) pgpasn_FreeBOOLEAN(ctx, local);
        return 0;
    }

    *boolstruct = local;
    return bytesused;
} /* pgpasn_UnpackBOOLEANInternal */

/************************************************************************
* Routines for PGPASN_BIT_STRING
*************************************************************************/

PGPASN_BIT_STRING *pgpasn_NewBIT_STRING(
    PGPASN_CONTEXT *ctx)
{
    PGPASN_BIT_STRING *f = NULL ;

    if (ctx == NULL)
        return NULL;

    f = (PGPASN_BIT_STRING *)PGPASN_Alloc(ctx->memMgr, sizeof(PGPASN_BIT_STRING) );
    if (f != NULL)
        memset( f, 0, sizeof(PGPASN_BIT_STRING) );

      return ((PGPASN_BIT_STRING *) f);
} /* pgpasn_NewBIT_STRING */

void pgpasn_DropInPlaceBIT_STRING(
    PGPASN_CONTEXT *ctx,
    PGPASN_BIT_STRING *f )
{
    if (ctx == NULL)
        return;

    if (f != NULL) {
        if ((f->val) != NULL) {
            PGPASN_Free(ctx->memMgr, f->val);
        }
        f->val = NULL ;
    }
    return ;
} /* pgpasn_DropInPlaceBIT_STRING */

void pgpasn_FreeBIT_STRING(
    PGPASN_CONTEXT *ctx,
    PGPASN_BIT_STRING *f )
{
    if (ctx == NULL)
        return;

    pgpasn_DropInPlaceBIT_STRING(ctx, f);
    if (f != NULL)
        PGPASN_Free(ctx->memMgr, f);
} /* pgpasn_FreeBIT_STRING */

size_t pgpasn_SizeofBIT_STRING(
    PGPASN_CONTEXT *ctx,
    PGPASN_BIT_STRING *bitblock,
    int outerSizeFlag)
{
    (void)ctx; /* for future use */

    return pgpasn_SizeofBIT_STRINGInternal(bitblock, outerSizeFlag, PGPASN_FALSE);
}

size_t pgpasn_SizeofBIT_STRINGInternal(
    PGPASN_BIT_STRING *bitblock,
    int outerSizeFlag,
    int expTaggedSize)
{
    size_t body_size;

    if (bitblock == NULL)
        return (0);

    body_size = 1 + bitblock->len;

    if (outerSizeFlag == PGPASN_TRUE)
        body_size = PGPASN_Tagged(body_size, 0);

    if (expTaggedSize == PGPASN_TRUE)
        body_size = PGPASN_Tagged(body_size, 0);

    return body_size;

} /* pgpasn_SizeofBIT_STRINGInternal */

size_t pgpasn_PackBIT_STRING(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_BIT_STRING *bitblock,
    int *erret)
{
    return (PackVariableBlock(ctx, PGPASN_ID_BIT_STRING, PGPASN_TRUE, 
                 (unsigned char)bitblock->nuub,
                 buf, buflen, (PGPASN_VariableBlock *)bitblock, erret));
}

size_t pgpasn_PackBIT_STRINGInternal(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_BIT_STRING *bitblock,
    unsigned char tag,
    int *erret)
{

    return (PackVariableBlock(ctx, tag, PGPASN_TRUE, 
                   (unsigned char)bitblock->nuub,
               buf, buflen, (PGPASN_VariableBlock *)bitblock, erret));

} /* pgpasn_PackBIT_STRINGInternal */


static size_t uipBitSegments(
    PGPASN_CONTEXT *ctx,
    int indefFlag,              /* is this an indefinite length block or not? */
    unsigned char exptag,    /* my expected block tag */
    PGPASN_BIT_STRING *bitblock,    /* output block */
    const unsigned char *buf,  /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    int *erret)            /* error return location */
{
    size_t bytesused = 0;
    size_t datasize;

    /* validity of erret and ctx checked in UnpkInPlaceBIT_STRING */

    if (buf == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoBlockPtr);
        return 0;
    }

    /* validity of bitblock checked in UnpkInPlaceBIT_STRING */
    bitblock->val = NULL;
    bitblock->len = 0;
    bitblock->nuub = 0;

    /* while there are segments */
    while (1) {

        /* For indef length end-of-contents is 0x00 0x00 and we
           have to eat those two bytes, otherwise we see if we've
           used up all the available data yet */
        if ( indefFlag == 1 &&
             (*(buf+bytesused) == 0x00 && *(buf+bytesused+1) == 0x00) ) {
            bytesused += 2;
            break;
        }
        else if (indefFlag == 0 && bytesused == buflen)
            break;

        if (*buf != exptag) {
            PGPASN_ERR(kPGPASNError_ErrUnpackInvalidEncoding);
            return 0;
        }
        bytesused++; /* skip the tag byte */

        datasize = 0;
        bytesused += PGPASN_GetLength(buf+bytesused, &datasize);

        if ((int)datasize == -1) { /* no nested indef lengths for bit string types */
            PGPASN_ERR(kPGPASNError_ErrUnpackInvalidEncoding);
            return 0;
        }

        if (bytesused + datasize > buflen) {
            PGPASN_ERR(kPGPASNError_ErrUnpackOverrun); /* note this error */
            return 0;
        }

        if (datasize == 0)
            continue;

        /* All segments but the last must be multiples of 8 bits, so
           the nuub value for all segments but the last must have 0
           for the nuub.  So we check for 0 nuub here, if the previous
           segment did not have 0 nuub, then its an error.  If this is
           the last segment, we will exit the loop so the new nuub can be other
           then 0. */
        if (bitblock->nuub == 0) {
            /* the first byte of the data is the number of unused bits */
            bitblock->nuub = *(buf+bytesused);
            bytesused++;

            PGPASN_Realloc(ctx->memMgr,
                      (void **)&bitblock->val, bitblock->len+datasize-1);
            if (bitblock->val == NULL) {
            PGPASN_ERR(kPGPASNError_ErrOutOfMemory);
            return 0;
            }

            memcpy(bitblock->val + bitblock->len,
               buf+bytesused, datasize-1);
            bitblock->len += datasize-1;
            bytesused += datasize-1;
        }
        else { /* error */
            PGPASN_ERR(kPGPASNError_ErrUnpackInvalidEncoding);
            return 0;
        }

    } /* while there are segments */

    return bytesused;
} /* UnpkInPlacebit_segments */

size_t pgpasn_UnpkInPlaceBIT_STRING(
    PGPASN_CONTEXT *ctx,
    PGPASN_BIT_STRING *bitstruct,    /* output block */
    const unsigned char *buf,   /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    unsigned char tag,
    int *erret)            /* error return location */
{
    size_t bytesused;
    size_t datasize;
    int constructed = 0;
    int indef = 0;

    if (erret == NULL)
        return 0;    /* can't report errors */
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (bitstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }

    PGPASN_TRACE_PRINT_FM(tag, 0x03, "BIT STRING");

    if (buf == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoBlockPtr);
        return 0;
    }
    if (buflen <= 0)
        return 0; /* no error -- no block */

    /* see note in UnpkInPlaceVariableBlock */
    if ( (*buf & 0xDF) != (tag & 0xDF) )
        return 0;
    if ( (*buf & 0x20) == 0x20)
        constructed = 1;

    bytesused = 1; /* eat the tag byte */

    bytesused += PGPASN_GetLength(buf+bytesused, &datasize);
    if ((int)datasize == -1) { /* indef length */
        datasize = 0;
        indef = 1;
    }

    if (bytesused+datasize > buflen) {
        PGPASN_ERR(kPGPASNError_ErrUnpackOverrun); /* note this error */
        return 0;
    }

    if (indef == 1 && constructed != 1) {
        PGPASN_ERR(kPGPASNError_ErrUnpackInvalidEncoding);
        return 0;
    }

    if (bitstruct->val != NULL)
        PGPASN_Free(ctx->memMgr, bitstruct->val);

    PGPASN_TRACE_PRINT_DATA(buf+bytesused,datasize);

    if (constructed == 0) { /* primitive */
        /* the first byte of the data is the number of unused bits */
        bitstruct->nuub = *(buf+bytesused);
        bytesused++;

        /* the len is -1 since we just used one byte for the nuub value */
        bitstruct->len = datasize-1;
        bitstruct->val = (unsigned char *)PGPASN_Alloc(ctx->memMgr, bitstruct->len);
        if (bitstruct->val == NULL) {
            PGPASN_ERR(kPGPASNError_ErrOutOfMemory);
            return 0;
        }
        memcpy(bitstruct->val, buf+bytesused, bitstruct->len);
        bytesused += bitstruct->len;
    }

    else if (indef == 1) { /* constructed, indefinite length */
        bytesused += uipBitSegments(ctx, indef, 
                           (unsigned char)(tag & 0xDF), bitstruct,
                                   buf+bytesused,
                       buflen-bytesused, erret);
    }

    else { /* constructed, definite length */
        bytesused += uipBitSegments(ctx, indef, 
                                   (unsigned char)(tag & 0xDF), bitstruct,
                                   buf+bytesused,
                       datasize, erret);
    }

    return bytesused;
} /* pgpasn_UnpkInPlaceBIT_STRING */

size_t pgpasn_UnpackBIT_STRING(
    PGPASN_CONTEXT *ctx,
    PGPASN_BIT_STRING **bitstruct,
    const unsigned char *buf,
    size_t buflen,
    int        *erret)
{
    return(pgpasn_UnpackBIT_STRINGInternal(ctx, bitstruct, buf, buflen,
                                  PGPASN_ID_BIT_STRING, erret));
}

size_t pgpasn_UnpackBIT_STRINGInternal(
    PGPASN_CONTEXT *ctx,
    PGPASN_BIT_STRING **bitstruct,
    const unsigned char *buf,
    size_t buflen,
    unsigned char  tag,
    int        *erret)    /* error return */
{
    size_t bytesused;
    PGPASN_BIT_STRING *local = NULL;

    if (erret == NULL)
        return 0;
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (bitstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    *bitstruct = NULL;

    if (buflen <= 0) return 0;

    /* see note in UnpkInPlaceVariableBlock */
    if ( (*buf & 0xDF) != (tag & 0xDF) ) return 0;

    local = pgpasn_NewBIT_STRING(ctx);
    bytesused = pgpasn_UnpkInPlaceBIT_STRING(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) pgpasn_FreeBIT_STRING(ctx, local);
        return 0;
    }

    *bitstruct = local;
    return bytesused;
} /* pgpasn_UnpackBIT_STRINGInternal */

/************************************************************************
* Routines for PGPASN_GeneralizedTime
*************************************************************************/

size_t pgpasn_PackGeneralizedTime(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_GeneralizedTime *timeblock,
    int *erret)
{
    return(PackVariableBlock(ctx, PGPASN_ID_GeneralizedTime, PGPASN_FALSE, 0, buf, buflen,
                        timeblock, erret));
}

size_t pgpasn_PackGeneralizedTimeInternal(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_GeneralizedTime *timeblock,
    unsigned char tag,
    int *erret)
{
  return(PackVariableBlock(ctx, tag, PGPASN_FALSE, 0, buf, buflen, timeblock, erret));

} /* pgpasn_PackGeneralizedTimeInternal */

size_t pgpasn_UnpkInPlaceGeneralizedTime(
    PGPASN_CONTEXT *ctx,
    PGPASN_GeneralizedTime *timestruct, /* output block */
    const unsigned char *buf,    /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    unsigned char tag,
    int *erret)         /* error return location */
{
  return(UnpkInPlaceVariableBlock(ctx, tag, "GeneralizedTime", 0x18, timestruct,
                     buf, buflen, erret));
} /* pgpasn_UnpkInPlaceGeneralizedTime */

size_t pgpasn_UnpackGeneralizedTime( 
    PGPASN_CONTEXT *ctx,
    PGPASN_GeneralizedTime **timestruct,
    const unsigned char *buf,
    size_t buflen,
    int        *erret)
{
    return(pgpasn_UnpackGeneralizedTimeInternal(ctx, timestruct, buf, buflen,
                      PGPASN_ID_GeneralizedTime, erret));
}

size_t pgpasn_UnpackGeneralizedTimeInternal( 
    PGPASN_CONTEXT *ctx,
    PGPASN_GeneralizedTime **timestruct,
    const unsigned char *buf,
    size_t buflen,
    unsigned char tag,
    int        *erret)  /* error return */
{
    size_t bytesused;
    PGPASN_GeneralizedTime *local = NULL;

    if (erret == NULL) return 0;
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (timestruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    *timestruct = NULL;

    if (buflen <= 0) return 0;

    /* see note in UnpkInPlaceVariableBlock */
    if ( (*buf & 0xDF) != (tag & 0xDF) ) return 0;

    local = pgpasn_NewGeneralizedTime(ctx);    /* carve a block for it */
    bytesused = pgpasn_UnpkInPlaceGeneralizedTime(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) pgpasn_FreeGeneralizedTime(ctx, local);
        return 0;
    }

    *timestruct = local;
    return bytesused;
} /* pgpasn_UnpackGeneralizedTimeInternal */

/************************************************************************
* Routines for PGPASN_IA5String
*************************************************************************/

size_t pgpasn_PackIA5String(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_IA5String *strblock,
    int *erret)
{
    return(PackVariableBlock(ctx, PGPASN_ID_IA5String, PGPASN_FALSE, 0, buf, buflen,
                        strblock, erret));

} /* pgpasn_PackIA5String */

size_t pgpasn_PackIA5StringInternal(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_IA5String *strblock,
    unsigned char tag,
    int *erret)
{
    return(PackVariableBlock(ctx, tag, PGPASN_FALSE, 0, buf, buflen, strblock, erret));

} /* pgpasn_PackIA5StringInternal */

size_t pgpasn_UnpkInPlaceIA5String(
    PGPASN_CONTEXT *ctx,
    PGPASN_IA5String *strblock,    /* output block */
    const unsigned char *buf, /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    unsigned char tag,
    int *erret)         /* error return location */
{
    return (UnpkInPlaceVariableBlock(ctx, tag, "IA5String", 0x16, strblock, buf, buflen, erret));
} /* pgpasn_UnpkInPlaceIA5String */

size_t pgpasn_UnpackIA5String(
    PGPASN_CONTEXT *ctx,
    PGPASN_IA5String **strblock,
    const unsigned char *buf,
    size_t buflen, /* my end pointer */
    int        *erret)
{
    return(pgpasn_UnpackIA5StringInternal(ctx, strblock, buf, buflen,
                                 PGPASN_ID_IA5String, erret));
}

size_t pgpasn_UnpackIA5StringInternal(
    PGPASN_CONTEXT *ctx,
    PGPASN_IA5String **strblock,
    const unsigned char *buf,
    size_t buflen, /* my end pointer */
    unsigned char tag,
    int        *erret)  /* error return */
{
    size_t bytesused;
    PGPASN_IA5String *local = NULL;

    if (erret == NULL) return 0;
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (strblock == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    *strblock = NULL;

    if (buflen <= 0) return 0;

    /* see note in UnpkInPlaceVariableBlock */
    if ( (*buf & 0xDF) != (tag & 0xDF) ) return 0;

    local = pgpasn_NewIA5String(ctx);    /* carve a block for it */
    bytesused = pgpasn_UnpkInPlaceIA5String(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) pgpasn_FreeIA5String(ctx, local);
        return 0;
    }

    *strblock = local;
    return bytesused;
} /* pgpasn_UnpackIA5StringInternal */

/************************************************************************
* Routines for PGPASN_INTEGER
*************************************************************************/

/************************************************************************
* normalize
*
*  remove leading 0's from the integer byte string/
*  This routine isn't the most efficient, but it should never have to
*  move any bytes because all numbers should be normalized already.
*************************************************************************/

static void normalize( PGPASN_INTEGER *b )
{
  size_t i ;
  unsigned char *x ;

  if (b == NULL) return ;
  if ((b->val) == NULL) {
    b->len = 0 ;        /* no bytes, no length */
    return ;
  }
  x = b->val ;            /* point to the array */
  while (   (((x[0]^x[1])&0x80) == 0)
         && ((x[0] == 0)||(x[0] == 0xff))
         && ((b->len) > 1) ) {    /* shorten this by 1 byte */
    (b->len)-- ;        /* note the shortening */
    for (i=0; i<(b->len); i++) x[i] = x[i+1] ; /* do it */
  } /* while */
  return ;  
} /* normalize */

size_t pgpasn_SizeofINTEGER(
    PGPASN_CONTEXT *ctx,
    PGPASN_INTEGER *intblock,
    int outerSizeFlag)
{
    (void)ctx; /* for future use */

    return pgpasn_SizeofINTEGERInternal(intblock, outerSizeFlag, PGPASN_FALSE);
}

size_t pgpasn_SizeofINTEGERInternal(
    PGPASN_INTEGER *intblock,
    int outerSizeFlag,
    int expTaggedSize)
{
    size_t length;

    if (intblock == NULL)
        return 0;

    normalize(intblock);

    length = intblock->len;

    if (outerSizeFlag == PGPASN_TRUE)
        length = length +  1 + PGPASN_LengthSize(length);

    if (expTaggedSize == PGPASN_TRUE)
        length = PGPASN_Tagged(length, 0);

    return length;

} /* pgpasn_SizeofINTEGERInternal */

size_t pgpasn_PackINTEGER(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_INTEGER *intblock,
    int *erret)
{
    return(pgpasn_PackINTEGERInternal(ctx, buf, buflen, intblock, PGPASN_ID_INTEGER, erret));
}

size_t pgpasn_PackINTEGERInternal(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_INTEGER *intblock,
    unsigned char tag,
    int *erret)
{
    if (intblock == NULL) return 0;
    normalize(intblock);

    return(PackVariableBlock(ctx, tag, PGPASN_FALSE, 0, buf, buflen, intblock, erret));

}

size_t pgpasn_UnpkInPlaceINTEGER(
    PGPASN_CONTEXT *ctx,
    PGPASN_INTEGER *intstruct,        /* output block */
    const unsigned char *buf,     /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    unsigned char tag,
    int *erret)         /* error return location */
{
    return (UnpkInPlaceVariableBlock(ctx, tag, "INTEGER", 0x02, intstruct, buf, buflen, erret));
} /* pgpasn_UnpkInPlaceINTEGER */

size_t pgpasn_UnpackINTEGER( 
    PGPASN_CONTEXT *ctx,
    PGPASN_INTEGER **intstruct,
    const unsigned char *buf,
    size_t buflen,
    int        *erret)
{
    return(pgpasn_UnpackINTEGERInternal(ctx, intstruct, buf, buflen, PGPASN_ID_INTEGER, erret));
}

size_t pgpasn_UnpackINTEGERInternal( 
    PGPASN_CONTEXT *ctx,
    PGPASN_INTEGER **intstruct,
    const unsigned char *buf,
    size_t buflen,
    unsigned char tag,
    int        *erret)  /* error return */
{
    size_t bytesused;
    PGPASN_INTEGER *local = NULL;

    if (erret == NULL) return 0;
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (intstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    *intstruct = NULL;

    if (buflen <= 0) return 0;

    if (*buf != tag) return 0;

    local = pgpasn_NewINTEGER(ctx);    /* carve a block for it */
    bytesused = pgpasn_UnpkInPlaceINTEGER(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) pgpasn_FreeINTEGER(ctx, local);
        return 0;
    }
  
    *intstruct = local;
    return bytesused;
} /* pgpasn_UnpackINTEGERInternal */

/************************************************************************
* pgpasn_GetIntVal
*
*  Extract a long from the PGPASN_INTEGER b.
*************************************************************************/

long pgpasn_GetIntVal(
    PGPASN_CONTEXT *ctx,
    PGPASN_INTEGER *b,
    int *error)
{
    long val = 0;
    size_t i;
    unsigned char *x;

    (void)ctx; /* for future use */

    if (error == NULL)
        return 0;
    *error = 0;

    if ( b == NULL || b->len > 4 || b->val == NULL ) {
        *error = -1;
        return 0;
    }

    x = b->val;

    for (i=0; i < b->len; i++) {
        val <<= 8;
        val += x[i];
    }

    return val;
} /* pgpasn_GetIntVal */

/************************************************************************
*  pgpasn_PutIntVal
*
*  Pack an integer value into a byte string
*************************************************************************/

int pgpasn_PutIntVal(
    PGPASN_CONTEXT *ctx,
    PGPASN_INTEGER *b, /* the block to take that value */
    long v        /* the value */ )
{
    long i ;
    unsigned char *x ;

    if (ctx == NULL)
        return kPGPASNError_ErrBadContext;

    if (b == NULL)
        return (-1);
    if ((b->val) != NULL)
        PGPASN_Free(ctx->memMgr, b->val);
    memset (b, 0, sizeof (PGPASN_INTEGER));
    b->val = x = (unsigned char *)PGPASN_Alloc(ctx->memMgr, 4);
    if (b->val == NULL)
        return (-1);
    b->len = 4 ;
    for (i=3; i >= 0; i-- ) {
        x[i] = v & 0xff ;
        v >>= 8 ;
    }
    normalize( b ) ;
    return 0;
} /* pgpasn_PutIntVal */

/************************************************************************
*  pgpasn_PutUIntBytes
*
*  Packs a large (greater than a C integer) unsigned integer value
*  into a PGPASN_INTEGER type.  This routine adds a leading zero byte if
*  the provided data's leading bit is set.
*************************************************************************/

int pgpasn_PutUIntBytes(
    PGPASN_CONTEXT *ctx,
    PGPASN_INTEGER *block, /* the block for the value */
    const unsigned char *value, /* the value */
    size_t lth) /* the length */
{
    size_t newLen = lth;

    if (ctx == NULL)
        return kPGPASNError_ErrBadContext;
    if (block == NULL)
        return (-1);

    if (block->val != NULL)
        PGPASN_Free(ctx->memMgr, block->val);
    memset (block, 0, sizeof(PGPASN_INTEGER));

    if ( (value[0] & 0x80) == 0x80 )
        newLen++;
    block->val = (unsigned char *)PGPASN_Alloc(ctx->memMgr, newLen);
    if (block->val == NULL)
        return (-1);
    memset (block->val, 0, newLen);

    if ( (value[0] & 0x80) == 0x80 )
        memcpy( block->val+1, value, lth);
    else
        memcpy( block->val, value, lth);
    block->len = newLen;
    return (0);
} /* pgpasn_PutOctVal */

/************************************************************************
* Routines for PGPASN_NumericString
*************************************************************************/

size_t pgpasn_PackNumericString(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_NumericString *numblock,
    int *erret )
{
    return(pgpasn_PackNumericStringInternal(ctx, buf, buflen, numblock,
                                    PGPASN_ID_NumericString, erret));
} /* pgpasn_PackNumericString */

size_t pgpasn_PackNumericStringInternal(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_NumericString *numblock,
    unsigned char tag,
    int *erret )
{
    size_t bytesused;
    size_t i;

    bytesused = PackVariableBlock(ctx, tag, PGPASN_FALSE,
                             0, buf, buflen, numblock, erret);
    if (bytesused == 0 || *erret != 0)
        return(bytesused);

    /* after the pack since then we know numblock is okay */
    for ( i = 0; i < numblock->len; i++ ) {
        if (isdigit(numblock->val[i]))
            continue;

        if (numblock->val[i] == (unsigned char)' ' /* space */ )
            continue;

        PGPASN_ERR(kPGPASNError_ErrBadNumericString);
        bytesused = 0;
        break;
    }

    return(bytesused);

} /* pgpasn_PackNumericStringInternal */

size_t pgpasn_UnpkInPlaceNumericString(
    PGPASN_CONTEXT *ctx,
    PGPASN_NumericString *numstruct,    /* output block */
    const unsigned char *buf,      /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    unsigned char tag,
    int *erret)          /* error return location */
{
    size_t bytesused;
    size_t i;

    bytesused = UnpkInPlaceVariableBlock(ctx, tag, "NumericString", 0x12, numstruct,
                            buf, buflen, erret);
    if (bytesused == 0 || *erret != 0)
       return bytesused;

    for ( i = 0; i < numstruct->len; i++ ) {
        if (isdigit(numstruct->val[i]))
            continue;

        if (numstruct->val[i] == (unsigned char)' ' /* space */ )
            continue;

        PGPASN_ERR(kPGPASNError_ErrBadNumericString);
        bytesused = 0;
        break;
    }

   return bytesused;
} /* pgpasn_UnpkInPlaceNumericString */

size_t pgpasn_UnpackNumericString(
    PGPASN_CONTEXT *ctx,
    PGPASN_NumericString **numstruct,
    const unsigned char *buf,
    size_t buflen,
    int       *erret)
{
    return(pgpasn_UnpackNumericStringInternal(ctx, numstruct, buf, buflen,
                PGPASN_ID_NumericString, erret));
}

size_t pgpasn_UnpackNumericStringInternal(
    PGPASN_CONTEXT *ctx,
    PGPASN_NumericString **numstruct,
    const unsigned char *buf,
    size_t buflen,
    unsigned char  tag,
    int       *erret)  /* error return */
{
    size_t bytesused;
    PGPASN_NumericString *local = NULL;

    if (erret == NULL) return 0;
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (numstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    *numstruct = NULL;

    if (buflen <= 0) return 0;

    /* see note in UnpkInPlaceVariableBlock */
    if ( (*buf & 0xDF) != (tag & 0xDF) ) return 0;

    local = pgpasn_NewNumericString(ctx);    /* carve a block for it */
    bytesused = pgpasn_UnpkInPlaceNumericString(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) pgpasn_FreeNumericString(ctx, local);
        return 0;
    }

    *numstruct = local;
    return bytesused;
} /* pgpasn_UnpackNumericStringInternal */

/************************************************************************
* Routines for PGPASN_OBJECT_ID
*************************************************************************/

size_t pgpasn_PackOBJECT_ID(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_OBJECT_ID *oidblock,
    int *erret)
{
  return(PackVariableBlock(ctx, PGPASN_ID_OBJECT_ID, PGPASN_FALSE, 0, buf, buflen,
                     oidblock, erret));

} 

size_t pgpasn_PackOBJECT_IDInternal(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_OBJECT_ID *oidblock,
    unsigned char tag,
    int *erret)
{
  return(PackVariableBlock(ctx, tag, PGPASN_FALSE, 0, buf, buflen, oidblock, erret));

} /* pgpasn_PackOBJECT_IDInternal */

int pgpasn_UnpkInPlaceOBJECT_ID(
    PGPASN_CONTEXT *ctx,
    PGPASN_OBJECT_ID *oidstruct,    /* output block */
    const unsigned char *buf,  /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    unsigned char tag,
    int *erret) 
{
  return (UnpkInPlaceVariableBlock(ctx, tag, "OBJECT IDENTIFIER", 0x06, oidstruct,
                      buf, buflen, erret));
} /* pgpasn_UnpkInPlaceOBJECT_ID */

size_t pgpasn_UnpackOBJECT_ID(
    PGPASN_CONTEXT *ctx,
    PGPASN_OBJECT_ID **oidstruct,
    const unsigned char *buf,
    size_t buflen, 
    int        *erret)
{
    return(pgpasn_UnpackOBJECT_IDInternal(ctx, oidstruct, buf, buflen,
            PGPASN_ID_OBJECT_ID, erret));
}

size_t pgpasn_UnpackOBJECT_IDInternal(
    PGPASN_CONTEXT *ctx,
    PGPASN_OBJECT_ID **oidstruct,
    const unsigned char *buf,
    size_t buflen,
    unsigned char tag,
    int        *erret)  /* error return */
{
    size_t bytesused;
    PGPASN_OBJECT_ID  *local = NULL;

    if (erret == NULL) return 0;
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (oidstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    *oidstruct = NULL;

    if (buflen <= 0) return 0;

    if (*buf != tag) return 0;

    local = pgpasn_NewOBJECT_ID(ctx);    /* carve a block for it */
    bytesused = pgpasn_UnpkInPlaceOBJECT_ID(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) pgpasn_FreeOBJECT_ID(ctx, local);
        return 0;
    }

    *oidstruct = local;
    return bytesused;
} /* pgpasn_UnpackOBJECT_IDInternal */

/************************************************************************
* Routines for PGPASN_OCTET_STRING
*************************************************************************/

size_t pgpasn_PackOCTET_STRING(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_OCTET_STRING *octblock,
    int *erret)
{
  return(PackVariableBlock(ctx, PGPASN_ID_OCTET_STRING, PGPASN_FALSE, 0, buf, buflen,
                      octblock, erret));
}

size_t pgpasn_PackOCTET_STRINGInternal(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_OCTET_STRING *octblock,
    unsigned char tag,
    int *erret)
{
  return(PackVariableBlock(ctx, tag, PGPASN_FALSE, 0, buf, buflen, octblock, erret));
} /* pgpasn_PackOCTET_STRINGInternal */

size_t pgpasn_UnpkInPlaceOCTET_STRING(
    PGPASN_CONTEXT *ctx,
    PGPASN_OCTET_STRING *octstruct,        /* output block */
    const unsigned char *buf,        /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    unsigned char tag,
    int *erret)     
{
  return (UnpkInPlaceVariableBlock(ctx, tag, "OCTET STRING", 0x04, octstruct,
                      buf, buflen, erret));
} /* pgpasn_UnpkInPlaceOCTET_STRING */

size_t pgpasn_UnpackOCTET_STRING(
    PGPASN_CONTEXT *ctx,
    PGPASN_OCTET_STRING **octstruct,
    const unsigned char *buf,
    size_t buflen, 
    int        *erret)
{
    return(pgpasn_UnpackOCTET_STRINGInternal(ctx, octstruct, buf, buflen,
            PGPASN_ID_OCTET_STRING, erret));
}

size_t pgpasn_UnpackOCTET_STRINGInternal(
    PGPASN_CONTEXT *ctx,
    PGPASN_OCTET_STRING **octstruct,
    const unsigned char *buf,
    size_t buflen,
    unsigned char tag,
    int        *erret)  /* error return */
{
    size_t bytesused;
    PGPASN_OCTET_STRING *local = NULL;

    if (erret == NULL) return 0;
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (octstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    *octstruct = NULL;

    if (buflen <= 0) return 0;

    /* see note in UnpkInPlaceVariableBlock */
    if ( (*buf & 0xDF) != (tag & 0xDF) ) return 0;

    local = pgpasn_NewOCTET_STRING(ctx);    /* carve a block for it */
    bytesused = pgpasn_UnpkInPlaceOCTET_STRING(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) pgpasn_FreeOCTET_STRING(ctx, local);
        return 0;
    }

    *octstruct = local;
    return bytesused;
} /* pgpasn_UnpackOCTET_STRINGInternal */

/************************************************************************
* Routines for PGPASN_PrintableString
*************************************************************************/

size_t pgpasn_PackPrintableString(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_PrintableString *strblock,
    int *erret )
{
    return(pgpasn_PackPrintableStringInternal(ctx, buf, buflen, strblock,
                 PGPASN_ID_PrintableString, erret));
} 

size_t pgpasn_PackPrintableStringInternal(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_PrintableString *strblock,
    unsigned char tag,
    int *erret )
{
    size_t bytesused;
    size_t i;

    bytesused = PackVariableBlock(ctx, tag, PGPASN_FALSE, 0,
                     buf, buflen, strblock, erret);
    if (bytesused == 0 || *erret != 0)
        return(bytesused);

    for ( i = 0; i < strblock->len; i++ ) {
        if ( isdigit( strblock->val[i] ) )
            continue;

        if ( isalpha( strblock->val[i] ) )
            continue;

        if ( strchr( "'\"()+,-./:=? ", strblock->val[i] ) != 0 )
            continue;

        PGPASN_ERR(kPGPASNError_ErrBadPrintableString);
        bytesused = 0;
        break;
    }

    return(bytesused);

} /* pgpasn_PackPrintableStringInternal */

int pgpasn_UnpkInPlacePrintableString(
    PGPASN_CONTEXT *ctx,
    PGPASN_PrintableString *printstruct,    /* output block */
    const unsigned char *buf,        /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    unsigned char tag,
    int *erret)     
{
    size_t bytesused;
    size_t i;

    bytesused = UnpkInPlaceVariableBlock(ctx, tag, "PrintableString", 0x13, printstruct,
                            buf, buflen, erret);
    if (bytesused == 0 || *erret != 0)
        return(bytesused);

    for ( i = 0; i < printstruct->len; i++ ) {
        if ( isdigit( printstruct->val[i] ) )
            continue;

        if ( isalpha( printstruct->val[i] ) )
            continue;

        if ( strchr( "'\"()+,-./:=? ", printstruct->val[i] ) != 0 )
            continue;

        PGPASN_ERR(kPGPASNError_ErrBadPrintableString);
        bytesused = 0;
        break;
    }

    return bytesused;
} /* pgpasn_UnpkInPlacePrintableString */

size_t pgpasn_UnpackPrintableString(
    PGPASN_CONTEXT *ctx,
    PGPASN_PrintableString **printstruct,
    const unsigned char *buf,
    size_t buflen,
    int       *erret)
{
    return(pgpasn_UnpackPrintableStringInternal(ctx, printstruct, buf, buflen,
                PGPASN_ID_PrintableString, erret));
}

size_t pgpasn_UnpackPrintableStringInternal(
    PGPASN_CONTEXT *ctx,
    PGPASN_PrintableString **printstruct,
    const unsigned char *buf,
    size_t buflen,
    unsigned char  tag,
    int       *erret)  /* error return */
{
    size_t bytesused;
    PGPASN_PrintableString *local = NULL;

    if (erret == NULL) return 0;
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (printstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    *printstruct = NULL;

    if (buflen <= 0) return 0;

    /* see note in UnpkInPlaceVariableBlock */
    if ( (*buf & 0xDF) != (tag & 0xDF) ) return 0;

    local = pgpasn_NewPrintableString(ctx);    /* carve a block for it */
    bytesused = pgpasn_UnpkInPlacePrintableString(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) pgpasn_FreePrintableString(ctx, local);
        return 0;
    }

    *printstruct = local;
    return bytesused;
} /* pgpasn_UnpackPrintablestringInternal */

/************************************************************************
* Routines for PGPASN_T61String
*************************************************************************/

size_t pgpasn_PackT61String(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_T61String *strblock,
    int *erret)
{
  return(PackVariableBlock(ctx, PGPASN_ID_T61String, PGPASN_FALSE, 0, buf, buflen,
                      strblock, erret));

} 

size_t pgpasn_PackT61StringInternal(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_T61String *strblock,
    unsigned char tag,
    int *erret)
{
  return(PackVariableBlock(ctx, tag, PGPASN_FALSE, 0, buf, buflen, strblock, erret));

}

size_t pgpasn_UnpkInPlaceT61String(
    PGPASN_CONTEXT *ctx,
    PGPASN_T61String *strstruct,        /* output block */
    const unsigned char *buf,      /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    unsigned char tag,
    int *erret) 
{
  return (UnpkInPlaceVariableBlock(ctx, tag, "T61String", 0x14, strstruct, buf, buflen, erret));
} /* pgpasn_UnpkInPlaceT61String */

size_t pgpasn_UnpackT61String(
    PGPASN_CONTEXT *ctx,
    PGPASN_T61String **strstruct,
    const unsigned char *buf,
    size_t buflen,
    int        *erret)
{
    return(pgpasn_UnpackT61StringInternal(ctx, strstruct, buf, buflen,
             PGPASN_ID_T61String, erret));   
}

size_t pgpasn_UnpackT61StringInternal(
    PGPASN_CONTEXT *ctx,
    PGPASN_T61String **strstruct,
    const unsigned char *buf,
    size_t buflen,
    unsigned char tag,
    int        *erret)  /* error return */
{
    size_t bytesused;
    PGPASN_T61String *local = NULL;

    if (erret == NULL) return 0;
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (strstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    *strstruct = NULL;

    if (buflen <= 0) return 0;

    /* see note in UnpkInPlaceVariableBlock */
    if ( (*buf & 0xDF) != (tag & 0xDF) ) return 0;

    local = pgpasn_NewT61String(ctx);    /* carve a block for it */
    bytesused = pgpasn_UnpkInPlaceT61String(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) pgpasn_FreeT61String(ctx, local);
        return 0;
    }

    *strstruct = local;
    return bytesused;
} /* pgpasn_UnpackT61StringInternal */

/************************************************************************
* Routines for PGPASN_ANY
*************************************************************************/

size_t pgpasn_SizeofANY(
    PGPASN_CONTEXT *ctx,
    PGPASN_ANY *block,
    int outerSizeFlag)
{
    (void)ctx; /* for future use */

    return pgpasn_SizeofANYInternal(block, outerSizeFlag, PGPASN_FALSE);
}

size_t pgpasn_SizeofANYInternal(
    PGPASN_ANY *block,
    int outerSizeFlag,
    int expTaggedSize)
{
    size_t length;

    /* for this inner always equals full, so we
       can ignore outerSizeFlag */
    (void)outerSizeFlag;

    if (block == NULL)
        return (0);

    length = block->len;


    /* and an ANY may be constructed */
    if (expTaggedSize == PGPASN_TRUE)
        length = PGPASN_Tagged(length, 0);

    return length;

} /* pgpasn_SizeofANYInternal */

size_t pgpasn_PackANY(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_ANY *block,
    int *erret)
{
    return(pgpasn_PackANYInternal(ctx, buf, buflen, block,
            PGPASN_ID_ANY, erret));
}

size_t pgpasn_PackANYInternal(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_ANY *block,
    unsigned char tag,
    int *erret)
{
    (void)ctx; /* for future use */

    if (erret == NULL) return 0;

    if (block == NULL) return 0;
    if (buf == NULL) return 0;
    if (block->len <= 0) return 0;
    if (block->val == NULL) return 0;
    if (block->len > buflen) {
        PGPASN_ERR(kPGPASNError_ErrPackOverrun);
        return 0;
    }

    if (tag == PGPASN_ID_ANY) {
        memcpy(buf, block->val, block->len);
        return block->len;
    }
    else {
        memcpy(buf, &tag, 1);
        memcpy(buf+1, block->val+1, block->len-1);
        return block->len;
    }

} /* pgpasn_PackANYInternal */

static size_t BerBlockSize(
    const unsigned char *buf,
    size_t buflen,
    int *erret)
{
    size_t bytesused;
    size_t datasize;

    if (erret == NULL)
        return 0;

    if (buf == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoBlockPtr);
        return 0;
    }

    if (buflen <= 0)
        return 0; /* no error -- no block */

    /* allow any tag */
    bytesused = 1;

    bytesused += PGPASN_GetLength(buf+bytesused, &datasize);

    if ((int)datasize >= 0)
        return (bytesused+datasize);

    /* else its indef length, so loop through all segments */
    while (1) {

        if ( *(buf+bytesused) == 0x00 && *(buf+bytesused+1) == 0x00 ) {
            bytesused += 2;
            break;
        }

        /* recursive call to process the segment */
        bytesused += BerBlockSize(buf + bytesused,
                     buflen - bytesused,
                     erret);
        if (*erret != 0)
            break;
        if (bytesused > buflen) {
            PGPASN_ERR(kPGPASNError_ErrUnpackOverrun);
            return 0;
        }

    }

    return bytesused;
} /* BerBlockSize */


size_t pgpasn_UnpkInPlaceANY(
    PGPASN_CONTEXT *ctx,
    PGPASN_ANY *asnstruct,    /* output block */
    const unsigned char *buf,    /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    unsigned char tag,
    int *erret)         /* error return location */
{
    size_t bytesused;
    size_t datasize;        /* the block data length */
    size_t lth;

    (void)tag; /* any tag allowed */

    if (erret == NULL) return 0;    /* can't report errors */
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (asnstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }

    if (buf == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoBlockPtr);
        return 0;
    }

    if (buflen <= 0) return 0; /* no error -- no block */

    PGPASN_TRACE_PRINT_FM(*buf, *buf, "ANY");

    bytesused = 1; /* accept ANY type byte */

    /* get the block length */
    bytesused += PGPASN_GetLength(buf+bytesused, &datasize);

    /* if this is indefinite length, we need to parse the ANY's
       DER data to get the block size.  DerBlockSize returns
       the length of the entire block, including the leading
       tag and size bytes */
    if ((int)datasize == -1) {
        datasize = 0;
        bytesused = BerBlockSize(buf, buflen, erret);
        if (*erret != 0)
            return 0;
    }

    lth = bytesused + datasize;
    if (lth > buflen) {
        PGPASN_ERR(kPGPASNError_ErrUnpackOverrun);
        return 0;
    }

    PGPASN_TRACE_PRINT_DATA(buf+bytesused,datasize);

    if ((asnstruct->val) != NULL)
        PGPASN_Free(ctx->memMgr, asnstruct->val);

    /* We just copy the entire block here, no lower level unpacking
       takes place; so, the size of the buffer needed is the data
       plus the tag and size bytes. */
    asnstruct->val = (unsigned char *)PGPASN_Alloc(ctx->memMgr, lth);
    if (asnstruct->val == NULL) {
        PGPASN_ERR(kPGPASNError_ErrOutOfMemory);
        return 0;
    }
    /* copy the whole structure */
    memcpy(asnstruct->val, buf, lth); 
    asnstruct->len = lth;

    return lth;
} /* pgpasn_UnpkInPlaceANY */

size_t pgpasn_UnpackANY(
    PGPASN_CONTEXT *ctx,
    PGPASN_ANY **asnstruct,
    const unsigned char *buf,
    size_t buflen,
    int        *erret)
{
    return(pgpasn_UnpackANYInternal(ctx, asnstruct, buf, buflen,
                PGPASN_ID_ANY, erret));
}

size_t pgpasn_UnpackANYInternal(
    PGPASN_CONTEXT *ctx,
    PGPASN_ANY **asnstruct,
    const unsigned char *buf,
    size_t buflen,
    unsigned char tag,
    int        *erret)  /* error return */
{
    size_t bytesused;
    PGPASN_ANY *local = NULL;

    if (erret == NULL) return 0;
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (asnstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    *asnstruct = NULL;

    if (buflen <= 0) return 0;

    local = pgpasn_NewANY(ctx);    /* carve a block for it */
    bytesused = pgpasn_UnpkInPlaceANY(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) pgpasn_FreeANY(ctx, local);
        return 0;
    }

    *asnstruct = local;
    return bytesused;
} /* pgpasn_UnpackANYInternal */

/*************************************************************************n* Routines for PGPASN_UTCTime
*************************************************************************/

size_t pgpasn_PackUTCTime(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_UTCTime *timeblock,
    int *erret)
{
  return(PackVariableBlock(ctx, PGPASN_ID_UTCTime, PGPASN_FALSE, 0, buf, buflen,
                      timeblock, erret));

} 

size_t pgpasn_PackUTCTimeInternal(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_UTCTime *timeblock,
    unsigned char tag,
    int *erret)
{
  return(PackVariableBlock(ctx, tag, PGPASN_FALSE, 0, buf, buflen, timeblock, erret));

}

size_t pgpasn_UnpkInPlaceUTCTime(
    PGPASN_CONTEXT *ctx,
    PGPASN_UTCTime *timestruct,    /* output block */
    const unsigned char *buf, /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    unsigned char tag,
    int *erret) 
{
  return (UnpkInPlaceVariableBlock(ctx, tag, "UTCTime", 0x17, timestruct, buf, buflen, erret));
} /* pgpasn_UnpkInPlaceUTCTime */

size_t pgpasn_UnpackUTCTime(
    PGPASN_CONTEXT *ctx,
    PGPASN_UTCTime **timestruct,
    const unsigned char *buf,
    size_t buflen, 
    int        *erret)
{
    return(pgpasn_UnpackUTCTimeInternal(ctx, timestruct, buf, buflen,
                               PGPASN_ID_UTCTime, erret));
}

size_t pgpasn_UnpackUTCTimeInternal(
    PGPASN_CONTEXT *ctx,
    PGPASN_UTCTime **timestruct,
    const unsigned char *buf,
    size_t buflen,
    unsigned char tag,
    int        *erret)  /* error return */
{
    size_t bytesused;
    PGPASN_UTCTime *local = NULL;

    if (erret == NULL) return 0;
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (timestruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    *timestruct = NULL;

    if (buflen <= 0) return 0;

    /* see note in UnpkInPlaceVariableBlock */
    if ( (*buf & 0xDF) != (tag & 0xDF) ) return 0;

    local = pgpasn_NewUTCTime(ctx);    /* carve a block for it */
    bytesused = pgpasn_UnpkInPlaceUTCTime(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) pgpasn_FreeUTCTime(ctx, local);
        return 0;
    }

    *timestruct = local;
    return bytesused;
} /* pgpasn_UnpackUTCTimeInternal */

/*************************************************************************
* Routines for PGPASN_VisibleString
*************************************************************************/

size_t pgpasn_PackVisibleString(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_VisibleString *strblock,
    int *erret)
{
  return(PackVariableBlock(ctx, PGPASN_ID_VisibleString, PGPASN_FALSE, 0, buf, buflen,
                      strblock, erret));

} 

size_t pgpasn_PackVisibleStringInternal(
    PGPASN_CONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PGPASN_VisibleString *strblock,
    unsigned char tag,
    int *erret)
{
  return(PackVariableBlock(ctx, tag, PGPASN_FALSE, 0, buf, buflen, strblock, erret));

}

size_t pgpasn_UnpkInPlaceVisibleString(
    PGPASN_CONTEXT *ctx,
    PGPASN_VisibleString *strstruct,        /* output block */
    const unsigned char *buf,        /* loc of input pointer */
    size_t buflen,        /* max end of my region */
    unsigned char tag,
    int *erret) 
{
  return (UnpkInPlaceVariableBlock(ctx, tag, "VisibleString", 0x1a, strstruct,
                      buf, buflen, erret));
} /* pgpasn_UnpkInPlaceVisibleString */

size_t pgpasn_UnpackVisibleString(
    PGPASN_CONTEXT *ctx,
    PGPASN_VisibleString **strstruct,
    const unsigned char *buf,
    size_t buflen,
    int        *erret)
{
    return(pgpasn_UnpackVisibleStringInternal(ctx, strstruct, buf, buflen,
             PGPASN_ID_VisibleString, erret));   
}

size_t pgpasn_UnpackVisibleStringInternal(
    PGPASN_CONTEXT *ctx,
    PGPASN_VisibleString **strstruct,
    const unsigned char *buf,
    size_t buflen,
    unsigned char tag,
    int        *erret)  /* error return */
{
    size_t bytesused;
    PGPASN_VisibleString *local = NULL;

    if (erret == NULL) return 0;
    *erret = 0;

    if (ctx == NULL) {
        PGPASN_ERR(kPGPASNError_ErrBadContext);
        return 0;
    }

    if (strstruct == NULL) {
        PGPASN_ERR(kPGPASNError_ErrUnpackNoStructure);
        return 0;
    }
    *strstruct = NULL;

    if (buflen <= 0) return 0;

    /* see note in UnpkInPlaceVariableBlock */
    if ( (*buf & 0xDF) != (tag & 0xDF) ) return 0;

    local = pgpasn_NewVisibleString(ctx);    /* carve a block for it */
    bytesused = pgpasn_UnpkInPlaceVisibleString(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) pgpasn_FreeVisibleString(ctx, local);
        return 0;
    }

    *strstruct = local;
    return bytesused;
} /* pgpasn_UnpackVisibleStringInternal */


#ifdef PGPASN_TRACE
#define PGPASN_TRACE_WIDTH 8

int PGPASN_TRACE_trval(enc, len, lev)
unsigned char *enc;
int len;
int lev;
{
    int n;
    int i;
    int j;

    for (n = 0; n < len; n++) {
        if ((n % PGPASN_TRACE_WIDTH) == 0) {
            fprintf(stderr, "\n");
            for (i=0; i<lev; i++) fprintf(stderr, "   ");
        }
        fprintf(stderr, "%02x ", enc[n]);
        if ((n % PGPASN_TRACE_WIDTH) == (PGPASN_TRACE_WIDTH-1)) {
            fprintf(stderr, "    ");
            for (i=n-(PGPASN_TRACE_WIDTH-1); i<=n; i++)
            if (isprint(enc[i])) fprintf(stderr, "%c", enc[i]);
             else fprintf(stderr, ".");
        }
    }
    if ((j = (n % PGPASN_TRACE_WIDTH)) != 0) {
        fprintf(stderr, "    ");
        for (i=0; i<PGPASN_TRACE_WIDTH-j; i++) fprintf(stderr, "   ");
        for (i=n-j; i<n; i++)
            if (isprint(enc[i])) fprintf(stderr, "%c", enc[i]);
            else fprintf(stderr, ".");
    }
    return(0);
}
#endif 

