/*******************************************************************
 *
 * 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 Mon May 17 09:25:53 1999
 *
 ******************************************************************/

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

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

#include "cert_util.h"

#ifdef PKITRACE
int PKITRACE_LEVEL = 0;
#endif

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

void PKIErrorHalt(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 *
PKIDefaultMemoryAllocationProc(
        PKIMemoryMgr *mgr,
        size_t requestSize)
{
    (void)mgr;

    return ( malloc(requestSize) );

} /* PKIDefaultMemoryAllocationProc */

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

    (void)mgr;

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

    return err;

} /* PKIMemoryMgrReallocationProc */

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

    free(allocation);

    return 0;

} /* PKIMemoryMgrDeallocationProc */

/* the default PKIMemoryMgr structure for the user */
PKIMemoryMgr PKIdefaultMemoryMgrStruct = {
        NULL,
        PKIDefaultMemoryAllocationProc,
        PKIDefaultMemoryReallocationProc,
        PKIDefaultMemoryDeallocationProc
};

/***************************************************************************
*  PKICompareElems
*  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 PKICompareElems(const void *a, const void *b)
{
    int length;
    PKIVariableBlock *locala = (PKIVariableBlock *)a;
    PKIVariableBlock *localb = (PKIVariableBlock *)b;

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

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

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

size_t PKILengthSize(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 PKITagged(size_t inner, int seqlike )
{
  if ( inner == 0 && ! seqlike )
      return (0);
  return ( 1 + inner + PKILengthSize(inner) );
} /* Tagged */

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

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

} /* PKIPutByte */

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

size_t PKIPutLength( unsigned char *buf, size_t length )
{
    unsigned char *bufptr = buf;
    size_t size = PKILengthSize(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);

} /* PKIPutLength */

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

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

} /* PKIGetByte */

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

size_t PKIGetLength(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 */
        PKITRACE_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 */

    PKITRACE_PRINT_LENGTH((int)x);

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

} /* PKIGetLength */

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

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

    bytesused = PKIPutByte(buf, tagbyte);
    bytesused += PKIPutLength(buf+bytesused, length);

    return bytesused;

} /* PKIPutTag */

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

size_t PKITakeTag(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 += PKIGetLength(buf+bytesused, length);

    return bytesused;
} /* PKITakeTag */

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

void *PKINewVariableBlock(PKICONTEXT *ctx)
{
    PKIVariableBlock *block = NULL ;

    if (ctx == NULL)
        return NULL;

    block = (PKIVariableBlock *) PKIAlloc(ctx->memMgr,
                                       sizeof(PKIVariableBlock) );

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

} /* PKINewVariableBlock */

size_t PKISizeofVariableBlock(PKICONTEXT *ctx,
                     PKIVariableBlock *block,
                     int outerSizeFlag)
{
    (void)ctx; /* for future use */

    return PKISizeofVariableBlockInternal(block, outerSizeFlag, PKIFALSE);
}

/*****
 *
 * internal PKISizeofVariableBlock
 *
 * 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 PKISizeofVariableBlockInternal(
    PKIVariableBlock *block,
    int outerSizeFlag,
    int expTaggedSize)
{
    size_t length;

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

    length = (size_t)block->len;

    if (outerSizeFlag == PKITRUE)
        length = length + 1 + PKILengthSize(block->len);

    if (expTaggedSize == PKITRUE)
        length = PKITagged(length, 0);

    return length;

} /* PKISizeofVariableBlockInternal */

void PKIDropInPlaceVariableBlock(PKICONTEXT *ctx,
                PKIVariableBlock *f )
{
    if (ctx == NULL)
        return;

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

void PKIFreeVariableBlock(PKICONTEXT *ctx,
                 PKIVariableBlock *f)
{
    if (ctx == NULL)
        return;

    PKIDropInPlaceVariableBlock(ctx, f);
    if (f != NULL)
        PKIFree(ctx->memMgr, f);

    return;
} /* PKIFreeVariableBlock */

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

static size_t UnpkInPlaceSegments(
    PKICONTEXT *ctx,
    int indefFlag,               /* is this an indefinite length block or not? */
    unsigned char exptag,      /* my expected block tag */
    PKIVariableBlock *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) {
        PKIERR(PKIErrUnpackNoBlockPtr);
        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) {
            PKIERR(PKIErrUnpackInvalidEncoding);
            return 0;
        }
        bytesused++; /* skip the tag byte */

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

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

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

        if (datasize == 0)
            continue;

        PKIRealloc(ctx->memMgr,
                  (void **)&asnblock->val, asnblock->len+datasize);
        if (asnblock->val == NULL) {
            PKIERR(PKIErrOutOfMemory);
            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(
    PKICONTEXT *ctx,
    unsigned char exptag, /* my expected block tag */
    const char *name,           /* my block name */
    unsigned char tag,    /* my real tag */
    PKIVariableBlock *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;

    PKITRACE_name(name);
    PKITRACE_PRINT_FM(exptag, tag, name);

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (asnblock == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    if (buf == NULL) {
        PKIERR(PKIErrUnpackNoBlockPtr);
        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 += PKIGetLength(buf+bytesused, &datasize);

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

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

    if (indef == 1 && constructed != 1) {
          PKIERR(PKIErrUnpackInvalidEncoding);
          return 0;
    }

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

    if (constructed == 0) { /* primitive */
          asnblock->len = datasize; /* record the length */
          asnblock->val = (unsigned char *)PKIAlloc(ctx->memMgr, datasize);
        if (asnblock->val == NULL) {
            PKIERR(PKIErrOutOfMemory);
            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 PKIVariableBlock -- 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(
    PKICONTEXT *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,
    PKIVariableBlock *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) {
        PKIERR(PKIErrPackNoBlock);
        return 0;
    }

    /* need room for tag + length bytes*/
    if (buflen < (1 + PKILengthSize(block->len+pad)) ) {
        PKIERR(PKIErrPackOverrun);
        return 0;
    }
    bytesused = PKIPutTag(buf, blockType, block->len+pad);

    if (pad) {

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

        if (buflen < (bytesused + 1 + block->len) ) {
            PKIERR(PKIErrPackOverrun);
            return 0;
        }
        /* add the pad byte before the data */
        bytesused += PKIPutByte( 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) ) {
            PKIERR(PKIErrPackOverrun);
              return 0;
        }
          /* just add the data */
          memcpy(buf+bytesused, block->val, block->len);
          bytesused += block->len;
    }

    return bytesused;

} /* PackVariableBlock */

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

int PKIPutOctVal(
    PKICONTEXT *ctx,
    PKIVariableBlock *block, /* the block for the value */
    const unsigned char *value, /* the value */
    size_t lth) /* the length */
{
    if (ctx == NULL)
        return PKIErrBadContext;
    if (block == NULL)
        return (-1);

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

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

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

int PKIPutStrVal(
    PKICONTEXT *ctx,
    PKIVariableBlock *block,        /* the block for the value */
    const char *value)    /* the value */                 
{
    if (ctx == NULL)
        return PKIErrBadContext;

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

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

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

    return (0);
} /* PKIPutStrVal */

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

char *PKIGetStrVal(
    PKICONTEXT *ctx,
    PKIVariableBlock *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 *)PKIAlloc(ctx->memMgr, block->len + 1);
    if (string == NULL)
        return NULL;

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

} /* PKIGetStrVal */

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

int PKIPutBitString (
    PKICONTEXT *ctx,
    PKIBitstringBlock *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 PKIErrBadContext;
    if (block == NULL)
        return (-1);

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

/************************************************************************
* PKIPutBoolVal
*
*  Put a boolean value in an PKIBOOLEAN block.
*************************************************************************/
int PKIPutBoolVal(
    PKICONTEXT *ctx,
    PKIBOOLEAN *block,    /* the block for the value */
    int val)        /*  */
{
    (void)ctx; /* for future use */

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

    if (val != 0) block->val = PKITRUE;
    else block->val = PKIFALSE;
    block->len = 1;

    return 0;
}

/************************************************************************
* PKIGetBoolVal
*
*  Put a boolean value in an PKIBOOLEAN block.
*************************************************************************/
int PKIGetBoolVal(
    PKICONTEXT *ctx,
    PKIBOOLEAN *block)    /* the block for the value */
{
    (void)ctx; /* for future use */

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

    if (block->val != 0) return PKITRUE;
    else return PKIFALSE;
}

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

PKINULL *PKINewNULL(
    PKICONTEXT *ctx)
{
    PKINULL *f = NULL;

    if (ctx == NULL)
        return NULL;

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

    return ((PKINULL *) f);
} /* PKINewNULL */

void PKIDropInPlaceNULL(
    PKICONTEXT *ctx,
    PKINULL *f)
{
    (void)ctx;
    (void)f;
     return ;
} /* PKIDropInPlaceNULL */

void PKIFreeNULL(
    PKICONTEXT *ctx,
    PKINULL *g)
{
    if (ctx == NULL)
        return;
    if (g != NULL)
        PKIFree(ctx->memMgr, g);
} /* PKIFreeNULL */

size_t PKISizeofNULL(
    PKICONTEXT *ctx,
    PKINULL *b,
    int outerSizeFlag)
{
    (void)ctx; /* for future use */

    return PKISizeofNULLInternal(b, outerSizeFlag, PKIFALSE);
} /* PKISizeofNULL */

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

    if (outerSizeFlag == PKITRUE)
          length = 2;

    if (expTaggedFlag == PKITRUE)
          length = PKITagged(length, 0);

    return length;

} /* PKISizeofNULLInternal */

size_t PKIPackNULL(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKINULL *asnblock,
    int *erret)
{
    return (PKIPackNULLInternal(ctx, buf, buflen, asnblock, PKIID_NULL, erret));
}

size_t PKIPackNULLInternal(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKINULL *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 = PKIPutByte(buf, tag);
    bytesused += PKIPutByte(buf+bytesused, 0x00); /* length = 0 bytes */

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

} /* PKIPackNULLInternal */

size_t PKIUnpkInPlaceNULL(
    PKICONTEXT *ctx,
    PKINULL *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;

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

    (void)ctx; 

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

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

    bytesused += PKIGetLength(buf+bytesused, &datasize);
    if (datasize != 0) {
        PKIERR(PKIErrUnpackNullLth);
        return 0;
    }

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

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

size_t PKIUnpackNULL(
    PKICONTEXT *ctx,
    PKINULL **nullstruct,
    const unsigned char *buf,
    size_t buflen, 
    int        *erret)  /* error return */
{
    return(PKIUnpackNULLInternal(ctx, nullstruct, buf, buflen, PKIID_NULL, erret));
} /* PKIUnpackNULL */

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

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (nullstruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    *nullstruct = NULL;

    if (buflen <= 0) return 0;

    if (*buf != tag) return 0;

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

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

/************************************************************************
* PKIBOOLEAN routines
*************************************************************************/

PKIBOOLEAN *PKINewBOOLEAN(
    PKICONTEXT *ctx)
{
    PKIBOOLEAN *f = NULL;

    if (ctx == NULL)
        return NULL;

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

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

void PKIDropInPlaceBOOLEAN(
    PKICONTEXT *ctx,
    PKIBOOLEAN *f)
{
    (void)ctx;
    (void)f;
    return ;
} /* PKIDropInPlaceBOOLEAN */

void PKIFreeBOOLEAN(
    PKICONTEXT *ctx,
    PKIBOOLEAN *g )
{
    if (ctx == NULL)
        return;
    if (g != NULL)
        PKIFree(ctx->memMgr, g);
} /* PKIFreeBOOLEAN */

size_t PKISizeofBOOLEAN(
    PKICONTEXT *ctx,
    PKIBOOLEAN *boolblock,
    int outerSizeFlag)
{
    (void)ctx; /* for future use */

    return PKISizeofBOOLEANInternal(boolblock, outerSizeFlag, PKIFALSE);
}

size_t PKISizeofBOOLEANInternal(
    PKIBOOLEAN *boolblock,
    int outerSizeFlag,
    int expTaggedSize)
{
    size_t length = 1;

    if (boolblock == NULL)
        return 0;

    if (outerSizeFlag == PKITRUE)
        length = 3;

    if (expTaggedSize == PKITRUE)
        length = PKITagged(length, 0);

    return length;

} /* PKISizeofBOOLEANInternal */

size_t PKIPackBOOLEAN(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIBOOLEAN *boolblock,
    int *erret )
{
    return(PKIPackBOOLEANInternal(ctx, buf, buflen, boolblock,  PKIID_BOOLEAN, erret));
} /* PKIPackBOOLEAN */

size_t PKIPackBOOLEANInternal(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIBOOLEAN *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 = PKIPutByte(buf, tag);    /* this is a PKIBOOLEAN */
    bytesused += PKIPutByte(buf+bytesused, 1);    /* length = 1 byte */
    bytesused += PKIPutByte(buf+bytesused, value); /* the value */

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

    return bytesused;
} /* PKIPackBOOLEANInternal */

size_t PKIUnpkInPlaceBOOLEAN(
    PKICONTEXT *ctx,
    PKIBOOLEAN *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; 

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

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

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

    bytesused += PKIGetLength(buf+bytesused, &datasize);
    if (datasize != 1) {
        PKIERR( PKIErrUnpackBooleanLth);
        return 0;
    }

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

    PKITRACE_PRINT_DATA(buf+bytesused,1);

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

    return bytesused;
} /* PKIUnpkInPlaceBOOLEAN */

size_t PKIUnpackBOOLEAN(
    PKICONTEXT *ctx,
    PKIBOOLEAN **boolstruct,
    const unsigned char *buf,
    size_t buflen,
    int        *erret)  /* error return */
{
    return(PKIUnpackBOOLEANInternal(ctx, boolstruct, buf, buflen,
                               PKIID_BOOLEAN, erret));
}

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

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (boolstruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    *boolstruct = NULL;

    if (buflen <= 0) return 0;

    if (*buf != tag) return 0;

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

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

/************************************************************************
* Routines for PKIBIT_STRING
*************************************************************************/

PKIBIT_STRING *PKINewBIT_STRING(
    PKICONTEXT *ctx)
{
    PKIBIT_STRING *f = NULL ;

    if (ctx == NULL)
        return NULL;

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

      return ((PKIBIT_STRING *) f);
} /* PKINewBIT_STRING */

void PKIDropInPlaceBIT_STRING(
    PKICONTEXT *ctx,
    PKIBIT_STRING *f )
{
    if (ctx == NULL)
        return;

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

void PKIFreeBIT_STRING(
    PKICONTEXT *ctx,
    PKIBIT_STRING *f )
{
    if (ctx == NULL)
        return;

    PKIDropInPlaceBIT_STRING(ctx, f);
    if (f != NULL)
        PKIFree(ctx->memMgr, f);
} /* PKIFreeBIT_STRING */

size_t PKISizeofBIT_STRING(
    PKICONTEXT *ctx,
    PKIBIT_STRING *bitblock,
    int outerSizeFlag)
{
    (void)ctx; /* for future use */

    return PKISizeofBIT_STRINGInternal(bitblock, outerSizeFlag, PKIFALSE);
}

size_t PKISizeofBIT_STRINGInternal(
    PKIBIT_STRING *bitblock,
    int outerSizeFlag,
    int expTaggedSize)
{
    size_t body_size;

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

    body_size = 1 + bitblock->len;

    if (outerSizeFlag == PKITRUE)
        body_size = PKITagged(body_size, 0);

    if (expTaggedSize == PKITRUE)
        body_size = PKITagged(body_size, 0);

    return body_size;

} /* PKISizeofBIT_STRINGInternal */

size_t PKIPackBIT_STRING(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIBIT_STRING *bitblock,
    int *erret)
{
    return (PackVariableBlock(ctx, PKIID_BIT_STRING, PKITRUE, 
                 (unsigned char)bitblock->nuub,
                 buf, buflen, (PKIVariableBlock *)bitblock, erret));
}

size_t PKIPackBIT_STRINGInternal(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIBIT_STRING *bitblock,
    unsigned char tag,
    int *erret)
{

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

} /* PKIPackBIT_STRINGInternal */


static size_t uipBitSegments(
    PKICONTEXT *ctx,
    int indefFlag,              /* is this an indefinite length block or not? */
    unsigned char exptag,    /* my expected block tag */
    PKIBIT_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) {
        PKIERR(PKIErrUnpackNoBlockPtr);
        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) {
            PKIERR(PKIErrUnpackInvalidEncoding);
            return 0;
        }
        bytesused++; /* skip the tag byte */

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

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

        if (bytesused + datasize > buflen) {
            PKIERR(PKIErrUnpackOverrun); /* 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++;

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

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

    } /* while there are segments */

    return bytesused;
} /* UnpkInPlacebit_segments */

size_t PKIUnpkInPlaceBIT_STRING(
    PKICONTEXT *ctx,
    PKIBIT_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) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (bitstruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }

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

    if (buf == NULL) {
        PKIERR(PKIErrUnpackNoBlockPtr);
        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 += PKIGetLength(buf+bytesused, &datasize);
    if ((int)datasize == -1) { /* indef length */
        datasize = 0;
        indef = 1;
    }

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

    if (indef == 1 && constructed != 1) {
        PKIERR(PKIErrUnpackInvalidEncoding);
        return 0;
    }

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

    PKITRACE_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 *)PKIAlloc(ctx->memMgr, bitstruct->len);
        if (bitstruct->val == NULL) {
            PKIERR(PKIErrOutOfMemory);
            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;
} /* PKIUnpkInPlaceBIT_STRING */

size_t PKIUnpackBIT_STRING(
    PKICONTEXT *ctx,
    PKIBIT_STRING **bitstruct,
    const unsigned char *buf,
    size_t buflen,
    int        *erret)
{
    return(PKIUnpackBIT_STRINGInternal(ctx, bitstruct, buf, buflen,
                                  PKIID_BIT_STRING, erret));
}

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

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (bitstruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    *bitstruct = NULL;

    if (buflen <= 0) return 0;

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

    local = PKINewBIT_STRING(ctx);
    bytesused = PKIUnpkInPlaceBIT_STRING(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) PKIFreeBIT_STRING(ctx, local);
        return 0;
    }

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

/************************************************************************
* Routines for PKIGeneralizedTime
*************************************************************************/

size_t PKIPackGeneralizedTime(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIGeneralizedTime *timeblock,
    int *erret)
{
    return(PackVariableBlock(ctx, PKIID_GeneralizedTime, PKIFALSE, 0, buf, buflen,
                        timeblock, erret));
}

size_t PKIPackGeneralizedTimeInternal(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIGeneralizedTime *timeblock,
    unsigned char tag,
    int *erret)
{
  return(PackVariableBlock(ctx, tag, PKIFALSE, 0, buf, buflen, timeblock, erret));

} /* PKIPackGeneralizedTimeInternal */

size_t PKIUnpkInPlaceGeneralizedTime(
    PKICONTEXT *ctx,
    PKIGeneralizedTime *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));
} /* PKIUnpkInPlaceGeneralizedTime */

size_t PKIUnpackGeneralizedTime( 
    PKICONTEXT *ctx,
    PKIGeneralizedTime **timestruct,
    const unsigned char *buf,
    size_t buflen,
    int        *erret)
{
    return(PKIUnpackGeneralizedTimeInternal(ctx, timestruct, buf, buflen,
                      PKIID_GeneralizedTime, erret));
}

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

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (timestruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    *timestruct = NULL;

    if (buflen <= 0) return 0;

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

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

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

/************************************************************************
* Routines for PKIIA5String
*************************************************************************/

size_t PKIPackIA5String(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIIA5String *strblock,
    int *erret)
{
    return(PackVariableBlock(ctx, PKIID_IA5String, PKIFALSE, 0, buf, buflen,
                        strblock, erret));

} /* PKIPackIA5String */

size_t PKIPackIA5StringInternal(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIIA5String *strblock,
    unsigned char tag,
    int *erret)
{
    return(PackVariableBlock(ctx, tag, PKIFALSE, 0, buf, buflen, strblock, erret));

} /* PKIPackIA5StringInternal */

size_t PKIUnpkInPlaceIA5String(
    PKICONTEXT *ctx,
    PKIIA5String *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));
} /* PKIUnpkInPlaceIA5String */

size_t PKIUnpackIA5String(
    PKICONTEXT *ctx,
    PKIIA5String **strblock,
    const unsigned char *buf,
    size_t buflen, /* my end pointer */
    int        *erret)
{
    return(PKIUnpackIA5StringInternal(ctx, strblock, buf, buflen,
                                 PKIID_IA5String, erret));
}

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

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (strblock == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    *strblock = NULL;

    if (buflen <= 0) return 0;

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

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

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

/************************************************************************
* Routines for PKIINTEGER
*************************************************************************/

/************************************************************************
* 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( PKIINTEGER *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 PKISizeofINTEGER(
    PKICONTEXT *ctx,
    PKIINTEGER *intblock,
    int outerSizeFlag)
{
    (void)ctx; /* for future use */

    return PKISizeofINTEGERInternal(intblock, outerSizeFlag, PKIFALSE);
}

size_t PKISizeofINTEGERInternal(
    PKIINTEGER *intblock,
    int outerSizeFlag,
    int expTaggedSize)
{
    size_t length;

    if (intblock == NULL)
        return 0;

    normalize(intblock);

    length = intblock->len;

    if (outerSizeFlag == PKITRUE)
        length = length +  1 + PKILengthSize(length);

    if (expTaggedSize == PKITRUE)
        length = PKITagged(length, 0);

    return length;

} /* PKISizeofINTEGERInternal */

size_t PKIPackINTEGER(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIINTEGER *intblock,
    int *erret)
{
    return(PKIPackINTEGERInternal(ctx, buf, buflen, intblock, PKIID_INTEGER, erret));
}

size_t PKIPackINTEGERInternal(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIINTEGER *intblock,
    unsigned char tag,
    int *erret)
{
    if (intblock == NULL) return 0;
    normalize(intblock);

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

}

size_t PKIUnpkInPlaceINTEGER(
    PKICONTEXT *ctx,
    PKIINTEGER *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));
} /* PKIUnpkInPlaceINTEGER */

size_t PKIUnpackINTEGER( 
    PKICONTEXT *ctx,
    PKIINTEGER **intstruct,
    const unsigned char *buf,
    size_t buflen,
    int        *erret)
{
    return(PKIUnpackINTEGERInternal(ctx, intstruct, buf, buflen, PKIID_INTEGER, erret));
}

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

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (intstruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    *intstruct = NULL;

    if (buflen <= 0) return 0;

    if (*buf != tag) return 0;

    local = PKINewINTEGER(ctx);    /* carve a block for it */
    bytesused = PKIUnpkInPlaceINTEGER(ctx, local, buf, buflen, tag, erret);
    if (*erret != 0) {
        if (local != NULL) PKIFreeINTEGER(ctx, local);
        return 0;
    }
  
    *intstruct = local;
    return bytesused;
} /* PKIUnpackINTEGERInternal */

/************************************************************************
* PKIGetIntVal
*
*  Extract a long from the PKIINTEGER b.
*************************************************************************/

long PKIGetIntVal(
    PKICONTEXT *ctx,
    PKIINTEGER *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;
} /* PKIGetIntVal */

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

int PKIPutIntVal(
    PKICONTEXT *ctx,
    PKIINTEGER *b, /* the block to take that value */
    long v        /* the value */ )
{
    long i ;
    unsigned char *x ;

    if (ctx == NULL)
        return PKIErrBadContext;

    if (b == NULL)
        return (-1);
    if ((b->val) != NULL)
        PKIFree(ctx->memMgr, b->val);
    memset (b, 0, sizeof (PKIINTEGER));
    b->val = x = (unsigned char *)PKIAlloc(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;
} /* PKIPutIntVal */

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

int PKIPutUIntBytes(
    PKICONTEXT *ctx,
    PKIINTEGER *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 PKIErrBadContext;
    if (block == NULL)
        return (-1);

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

    if ( (value[0] & 0x80) == 0x80 )
        newLen++;
    block->val = (unsigned char *)PKIAlloc(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);
} /* PKIPutOctVal */

/************************************************************************
* Routines for PKINumericString
*************************************************************************/

size_t PKIPackNumericString(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKINumericString *numblock,
    int *erret )
{
    return(PKIPackNumericStringInternal(ctx, buf, buflen, numblock,
                                    PKIID_NumericString, erret));
} /* PKIPackNumericString */

size_t PKIPackNumericStringInternal(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKINumericString *numblock,
    unsigned char tag,
    int *erret )
{
    size_t bytesused;
    size_t i;

    bytesused = PackVariableBlock(ctx, tag, PKIFALSE,
                             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;

        PKIERR(PKIErrBadNumericString);
        bytesused = 0;
        break;
    }

    return(bytesused);

} /* PKIPackNumericStringInternal */

size_t PKIUnpkInPlaceNumericString(
    PKICONTEXT *ctx,
    PKINumericString *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;

        PKIERR(PKIErrBadNumericString);
        bytesused = 0;
        break;
    }

   return bytesused;
} /* PKIUnpkInPlaceNumericString */

size_t PKIUnpackNumericString(
    PKICONTEXT *ctx,
    PKINumericString **numstruct,
    const unsigned char *buf,
    size_t buflen,
    int       *erret)
{
    return(PKIUnpackNumericStringInternal(ctx, numstruct, buf, buflen,
                PKIID_NumericString, erret));
}

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

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (numstruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    *numstruct = NULL;

    if (buflen <= 0) return 0;

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

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

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

/************************************************************************
* Routines for PKIOBJECT_ID
*************************************************************************/

size_t PKIPackOBJECT_ID(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIOBJECT_ID *oidblock,
    int *erret)
{
  return(PackVariableBlock(ctx, PKIID_OBJECT_ID, PKIFALSE, 0, buf, buflen,
                     oidblock, erret));

} 

size_t PKIPackOBJECT_IDInternal(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIOBJECT_ID *oidblock,
    unsigned char tag,
    int *erret)
{
  return(PackVariableBlock(ctx, tag, PKIFALSE, 0, buf, buflen, oidblock, erret));

} /* PKIPackOBJECT_IDInternal */

int PKIUnpkInPlaceOBJECT_ID(
    PKICONTEXT *ctx,
    PKIOBJECT_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));
} /* PKIUnpkInPlaceOBJECT_ID */

size_t PKIUnpackOBJECT_ID(
    PKICONTEXT *ctx,
    PKIOBJECT_ID **oidstruct,
    const unsigned char *buf,
    size_t buflen, 
    int        *erret)
{
    return(PKIUnpackOBJECT_IDInternal(ctx, oidstruct, buf, buflen,
            PKIID_OBJECT_ID, erret));
}

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

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (oidstruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    *oidstruct = NULL;

    if (buflen <= 0) return 0;

    if (*buf != tag) return 0;

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

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

/************************************************************************
* Routines for PKIOCTET_STRING
*************************************************************************/

size_t PKIPackOCTET_STRING(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIOCTET_STRING *octblock,
    int *erret)
{
  return(PackVariableBlock(ctx, PKIID_OCTET_STRING, PKIFALSE, 0, buf, buflen,
                      octblock, erret));
}

size_t PKIPackOCTET_STRINGInternal(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIOCTET_STRING *octblock,
    unsigned char tag,
    int *erret)
{
  return(PackVariableBlock(ctx, tag, PKIFALSE, 0, buf, buflen, octblock, erret));
} /* PKIPackOCTET_STRINGInternal */

size_t PKIUnpkInPlaceOCTET_STRING(
    PKICONTEXT *ctx,
    PKIOCTET_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));
} /* PKIUnpkInPlaceOCTET_STRING */

size_t PKIUnpackOCTET_STRING(
    PKICONTEXT *ctx,
    PKIOCTET_STRING **octstruct,
    const unsigned char *buf,
    size_t buflen, 
    int        *erret)
{
    return(PKIUnpackOCTET_STRINGInternal(ctx, octstruct, buf, buflen,
            PKIID_OCTET_STRING, erret));
}

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

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (octstruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    *octstruct = NULL;

    if (buflen <= 0) return 0;

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

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

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

/************************************************************************
* Routines for PKIPrintableString
*************************************************************************/

size_t PKIPackPrintableString(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIPrintableString *strblock,
    int *erret )
{
    return(PKIPackPrintableStringInternal(ctx, buf, buflen, strblock,
                 PKIID_PrintableString, erret));
} 

size_t PKIPackPrintableStringInternal(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIPrintableString *strblock,
    unsigned char tag,
    int *erret )
{
    size_t bytesused;
    size_t i;

    bytesused = PackVariableBlock(ctx, tag, PKIFALSE, 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;

        PKIERR(PKIErrBadPrintableString);
        bytesused = 0;
        break;
    }

    return(bytesused);

} /* PKIPackPrintableStringInternal */

int PKIUnpkInPlacePrintableString(
    PKICONTEXT *ctx,
    PKIPrintableString *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;

        PKIERR(PKIErrBadPrintableString);
        bytesused = 0;
        break;
    }

    return bytesused;
} /* PKIUnpkInPlacePrintableString */

size_t PKIUnpackPrintableString(
    PKICONTEXT *ctx,
    PKIPrintableString **printstruct,
    const unsigned char *buf,
    size_t buflen,
    int       *erret)
{
    return(PKIUnpackPrintableStringInternal(ctx, printstruct, buf, buflen,
                PKIID_PrintableString, erret));
}

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

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (printstruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    *printstruct = NULL;

    if (buflen <= 0) return 0;

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

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

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

/************************************************************************
* Routines for PKIT61String
*************************************************************************/

size_t PKIPackT61String(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIT61String *strblock,
    int *erret)
{
  return(PackVariableBlock(ctx, PKIID_T61String, PKIFALSE, 0, buf, buflen,
                      strblock, erret));

} 

size_t PKIPackT61StringInternal(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIT61String *strblock,
    unsigned char tag,
    int *erret)
{
  return(PackVariableBlock(ctx, tag, PKIFALSE, 0, buf, buflen, strblock, erret));

}

size_t PKIUnpkInPlaceT61String(
    PKICONTEXT *ctx,
    PKIT61String *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));
} /* PKIUnpkInPlaceT61String */

size_t PKIUnpackT61String(
    PKICONTEXT *ctx,
    PKIT61String **strstruct,
    const unsigned char *buf,
    size_t buflen,
    int        *erret)
{
    return(PKIUnpackT61StringInternal(ctx, strstruct, buf, buflen,
             PKIID_T61String, erret));   
}

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

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (strstruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    *strstruct = NULL;

    if (buflen <= 0) return 0;

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

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

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

/************************************************************************
* Routines for PKIANY
*************************************************************************/

size_t PKISizeofANY(
    PKICONTEXT *ctx,
    PKIANY *block,
    int outerSizeFlag)
{
    (void)ctx; /* for future use */

    return PKISizeofANYInternal(block, outerSizeFlag, PKIFALSE);
}

size_t PKISizeofANYInternal(
    PKIANY *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 == PKITRUE)
        length = PKITagged(length, 0);

    return length;

} /* PKISizeofANYInternal */

size_t PKIPackANY(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIANY *block,
    int *erret)
{
    return(PKIPackANYInternal(ctx, buf, buflen, block,
            PKIID_ANY, erret));
}

size_t PKIPackANYInternal(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIANY *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) {
        PKIERR(PKIErrPackOverrun);
        return 0;
    }

    if (tag == PKIID_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;
    }

} /* PKIPackANYInternal */

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) {
        PKIERR(PKIErrUnpackNoBlockPtr);
        return 0;
    }

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

    /* allow any tag */
    bytesused = 1;

    bytesused += PKIGetLength(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) {
            PKIERR(PKIErrUnpackOverrun);
            return 0;
        }

    }

    return bytesused;
} /* BerBlockSize */


size_t PKIUnpkInPlaceANY(
    PKICONTEXT *ctx,
    PKIANY *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) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (asnstruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }

    if (buf == NULL) {
        PKIERR(PKIErrUnpackNoBlockPtr);
        return 0;
    }

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

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

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

    /* get the block length */
    bytesused += PKIGetLength(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) {
        PKIERR(PKIErrUnpackOverrun);
        return 0;
    }

    PKITRACE_PRINT_DATA(buf+bytesused,datasize);

    if ((asnstruct->val) != NULL)
        PKIFree(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 *)PKIAlloc(ctx->memMgr, lth);
    if (asnstruct->val == NULL) {
        PKIERR(PKIErrOutOfMemory);
        return 0;
    }
    /* copy the whole structure */
    memcpy(asnstruct->val, buf, lth); 
    asnstruct->len = lth;

    return lth;
} /* PKIUnpkInPlaceANY */

size_t PKIUnpackANY(
    PKICONTEXT *ctx,
    PKIANY **asnstruct,
    const unsigned char *buf,
    size_t buflen,
    int        *erret)
{
    return(PKIUnpackANYInternal(ctx, asnstruct, buf, buflen,
                PKIID_ANY, erret));
}

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

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (asnstruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    *asnstruct = NULL;

    if (buflen <= 0) return 0;

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

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

/*************************************************************************n* Routines for PKIUTCTime
*************************************************************************/

size_t PKIPackUTCTime(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIUTCTime *timeblock,
    int *erret)
{
  return(PackVariableBlock(ctx, PKIID_UTCTime, PKIFALSE, 0, buf, buflen,
                      timeblock, erret));

} 

size_t PKIPackUTCTimeInternal(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIUTCTime *timeblock,
    unsigned char tag,
    int *erret)
{
  return(PackVariableBlock(ctx, tag, PKIFALSE, 0, buf, buflen, timeblock, erret));

}

size_t PKIUnpkInPlaceUTCTime(
    PKICONTEXT *ctx,
    PKIUTCTime *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));
} /* PKIUnpkInPlaceUTCTime */

size_t PKIUnpackUTCTime(
    PKICONTEXT *ctx,
    PKIUTCTime **timestruct,
    const unsigned char *buf,
    size_t buflen, 
    int        *erret)
{
    return(PKIUnpackUTCTimeInternal(ctx, timestruct, buf, buflen,
                               PKIID_UTCTime, erret));
}

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

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (timestruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    *timestruct = NULL;

    if (buflen <= 0) return 0;

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

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

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

/*************************************************************************
* Routines for PKIVisibleString
*************************************************************************/

size_t PKIPackVisibleString(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIVisibleString *strblock,
    int *erret)
{
  return(PackVariableBlock(ctx, PKIID_VisibleString, PKIFALSE, 0, buf, buflen,
                      strblock, erret));

} 

size_t PKIPackVisibleStringInternal(
    PKICONTEXT *ctx,
    unsigned char *buf,
    size_t buflen,
    PKIVisibleString *strblock,
    unsigned char tag,
    int *erret)
{
  return(PackVariableBlock(ctx, tag, PKIFALSE, 0, buf, buflen, strblock, erret));

}

size_t PKIUnpkInPlaceVisibleString(
    PKICONTEXT *ctx,
    PKIVisibleString *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));
} /* PKIUnpkInPlaceVisibleString */

size_t PKIUnpackVisibleString(
    PKICONTEXT *ctx,
    PKIVisibleString **strstruct,
    const unsigned char *buf,
    size_t buflen,
    int        *erret)
{
    return(PKIUnpackVisibleStringInternal(ctx, strstruct, buf, buflen,
             PKIID_VisibleString, erret));   
}

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

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

    if (ctx == NULL) {
        PKIERR(PKIErrBadContext);
        return 0;
    }

    if (strstruct == NULL) {
        PKIERR(PKIErrUnpackNoStructure);
        return 0;
    }
    *strstruct = NULL;

    if (buflen <= 0) return 0;

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

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

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


#ifdef PKITRACE
#define PKITRACE_WIDTH 8

int PKITRACE_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 % PKITRACE_WIDTH) == 0) {
            fprintf(stderr, "\n");
            for (i=0; i<lev; i++) fprintf(stderr, "   ");
        }
        fprintf(stderr, "%02x ", enc[n]);
        if ((n % PKITRACE_WIDTH) == (PKITRACE_WIDTH-1)) {
            fprintf(stderr, "    ");
            for (i=n-(PKITRACE_WIDTH-1); i<=n; i++)
            if (isprint(enc[i])) fprintf(stderr, "%c", enc[i]);
             else fprintf(stderr, ".");
        }
    }
    if ((j = (n % PKITRACE_WIDTH)) != 0) {
        fprintf(stderr, "    ");
        for (i=0; i<PKITRACE_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 

