/* 0950, Tue 21 Sep 99

   ASN1.C:  Routines to handle ASN.1 BER -
               'parse' routines get values from a packet,
	       'build' routines put values into a packet.
            NeTraMet version, based on CMU SNMPv2 (see below).

   Copyright (C) 1992-2002 by Nevil Brownlee,
   CAIDA | University of Auckland */

/* 
 * $Log: asn1.c,v $
 * Revision 1.1.1.2.2.8  2002/02/23 01:57:35  nevil
 * Moving srl examples to examples/ directory.  Modified examples/Makefile.in
 *
 * Revision 1.1.1.2.2.4  2000/06/06 03:38:24  nevil
 * Combine NEW_ATR with TCP_ATR, various bug fixes
 *
 * Revision 1.1.1.2.2.1  1999/11/29 00:17:28  nevil
 * Make changes to support NetBSD on an Alpha (see version.history for details)
 *
 * Revision 1.1.1.2  1999/10/03 21:06:28  nevil
 * *** empty log message ***
 *
 * Revision 1.1.1.1.2.3  1999/09/24 02:58:41  nevil
 * Polish up code to get rid of warning messages from Borland (DOS) compiler.
 * Make manager PeerAddress buffers NSAP_ADDR_LEN bytes long.
 * Add asn_lookup variable - only call FindSubnet if ruleset uses ASNs.
 *
 * Revision 1.1.1.1.2.2  1999/09/22 05:38:45  nevil
 * Improve code to work properly on 64-bit machines
 * - Add OS=ALPHA handling to configure.in
 * - Clean up the Alpha compiler warnings
 * - Change all the snmp-related code to use Bit32 instead of unsigned long
 *
 * Revision 1.1.1.1.2.1  1999/01/08 01:38:35  nevil
 * Distribution file for 4.3b7
 *
 * Revision 1.1.1.1  1998/11/16 03:57:30  nevil
 * Import of NeTraMet 4.3b3
 *
 * Revision 1.1.1.1  1998/11/16 03:22:02  nevil
 * Import of release 4.3b3
 *
 * Revision 1.1.1.1  1998/10/28 20:31:30  nevil
 * Import of NeTraMet 4.3b1
 *
 * Revision 1.1.3.2  1998/10/14 04:13:42  nevil
 * Merge Nicolai's patches into 4.2.1 distribution
 *
 * Revision 1.1.1.1  1998/10/13 01:35:00  nevil
 * Import of NeTraMet 4.2.1
 *
 * Revision 1.1.1.1  1998/08/24 12:09:30  nguba
 * NetraMet 4.2 Original Distribution
 */

/**********************************************************************
	Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University

                      All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the name of CMU not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
******************************************************************/

#if HAVE_CONFIG_H
#include <ntm_conf.h>
#endif

#define TESTING 0

#include <stdlib.h>
#include <stdio.h>  /* Used by ERROR() define */
#include <ctype.h>

#if !defined(DOS)
#include <sys/types.h>
#include <netinet/in.h>
#endif

#ifdef vms
#include <in.h>
#endif

#include "ausnmp.h"
#include "asn1.h"
#include "snmp.h"
#include "snmpimpl.h"


/*
 * asn_parse_int - pulls a signed 32-bit integer out of an ASN int type.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the end of this object.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns NULL on any error.
 */
u_char *asn_parse_int(
   u_char    *data,	   /* IN - pointer to start of object */
   int       *datalength,  /* IN/OUT - number of valid bytes left in buffer */
   u_char    *type,	   /* OUT - asn type of object */
   Bit32 far *intp,	   /* IN/OUT - pointer to start of output buffer */
   int        intsize)     /* IN - size of output buffer */
{
/*
 * ASN.1 integer ::= 0x02 asnlength byte {byte}*
 */
   u_char *bufp = data;
   Bit32 asn_length;
   Bit32 intval = 0;

#if TESTING
int j;
printf("asn_parse_int(1): data=%p, bufp=%p, *datalength=%d, intsize=%d\n", 
   data, bufp, *datalength, intsize);
printf("   *intp =");
for (j=0; j != intsize; ++j) printf(" %0.2x", data[j]);
printf("\n");
#endif
   if (intsize != sizeof(intval)) {
      ERROR("size not long");
      return NULL;
      }
   *type = *bufp++;
   bufp = asn_parse_length(bufp, &asn_length);
   if (bufp == NULL) {
      ERROR("bad length");
      return NULL;
      }
#if TESTING
printf("asn_parse_int(2): data=%p, bufp=%p, *datalength=%d\n", 
   data, bufp, *datalength);
#endif
   if (asn_length + (bufp - data) > *datalength) {
      ERROR("overflow of message");
      return NULL;
      }
   if (asn_length > intsize) {
      ERROR("integer too large");
      return NULL;
      }
   *datalength -= (int)asn_length + (bufp - data);
   if (*bufp & 0x80)
      intval = -1; /* Integer is negative */
   while(asn_length--)
      intval = (intval << 8) | *bufp++;
   *intp = intval;
#if TESTING
printf("asn_parse_int(3): intval=%d, data=%p, bufp=%p, *datalength=%d\n", 
   intval, data, bufp, *datalength);
#endif
   return bufp;
   }


/*
 * asn_parse_unsigned_int - pulls a Bit32 out of an ASN int type.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the end of this object.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns NULL on any error.
 */
u_char *asn_parse_unsigned_int(
   u_char    *data,	   /* IN - pointer to start of object */
   int	     *datalength,  /* IN/OUT - number of valid bytes left in buffer */
   u_char    *type,	   /* OUT - asn type of object */
   Bit32 far *intp,	   /* IN/OUT - pointer to start of output buffer */
   int	      intsize)     /* IN - size of output buffer */
{
/*
 * ASN.1 integer ::= 0x02 asnlength byte {byte}*
 */
   u_char *bufp = data;
   Bit32 asn_length;
   Bit32 intval = 0;

   if (intsize != sizeof(intval)) {
      ERROR("size not long");
      return NULL;
     }
   *type = *bufp++;
   bufp = asn_parse_length(bufp, &asn_length);
   if (bufp == NULL) {
      ERROR("bad length");
      return NULL;
      }
   if (asn_length + (bufp - data) > *datalength) {
      ERROR("overflow of message");
      return NULL;
      }
   if ((asn_length > (intsize + 1)) ||
         ((asn_length == intsize + 1) && *bufp != 0x00)) {
      ERROR("unsigned integer too large");
      return NULL;
      }
   *datalength -= (int)asn_length + (bufp - data);
   while(asn_length--)
      intval = (intval << 8) | *bufp++;
   *intp = intval;
   return bufp;
   }


/*
 * asn_build_int - builds an ASN object containing a signed 32-bit integer.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the end of this object.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns NULL on any error.
 */
u_char *asn_build_int(
   u_char    *data,	   /* IN - pointer to start of output buffer */
   int       *datalength,  /* IN/OUT - number of valid bytes left in buffer */
   u_char     type,	   /* IN - asn type of object */
   Bit32 far *intp,	   /* IN - pointer to start of long integer */
   int        intsize)     /* IN - size of *intp */
{
/*
 * ASN.1 integer ::= 0x02 asnlength byte {byte}*
 */

   Int32 intval;
   Bit32 mask;
   Bit8 c;  Bit16 s;  /* UA: Handle 1 and 2-byte integers */

#if TESTING > 1
   scpos(0,24);
   printf("build_int(0): type=%d, size=%d\n",
      type,intsize);
#endif
   if (intsize == sizeof(intval)) intval = (Int32)*intp;
   else if (intsize == sizeof(c)) {
      c = *((char far *)intp);  intval = (Int32)c;
      }
   else if (intsize == sizeof(s)) {
      s = *((short far *)intp);  intval = (Int32)s;
      }
   else return NULL;
#if TESTING
   scpos(0,24);
   printf("build_int(1): type=%d, size=%d, intval=%d\n",
      type,intsize, intval);
#endif
   intsize = sizeof(intval);
   /*
    * Truncate "unnecessary" bytes off of the most significant end of
    * this 2's complement integer.  There should be no sequence of 9
    * consecutive 1's or 0's at the most significant end of the integer.
    */
   mask = 0x1FFL << ((8 * (sizeof(intval) - 1)) - 1);
   /* mask is 0xFF800000 on a 32-bit machine */
   while((((intval & mask) == 0) || ((intval & mask) == mask))
         && intsize > 1) {
      intsize--;
      intval <<= 8;
      }
   data = asn_build_header(data, datalength, type, intsize);
   if (data == NULL)
      return NULL;
   if (*datalength < intsize)
      return NULL;
   *datalength -= intsize;
   mask = 0xFFL << (8 * (sizeof(intval) - 1));
   /* mask is 0xFF000000 on a 32-bit machine */
   while (intsize--) {
      *data++ = (u_char)((intval & mask) >> (8 * (sizeof(intval) - 1)));
      intval <<= 8;
      }
   return data;
   }


/*
 * asn_build_unsigned_int - builds an ASN object containing a Bit32.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the end of this object.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns NULL on any error.
 */
u_char *asn_build_unsigned_int(
   u_char    *data,  	   /* IN - pointer to start of output buffer */
   int       *datalength,  /* IN/OUT - number of valid bytes left in buffer */
   u_char     type,  	   /* IN - asn type of object */
   Bit32 far *intp,	   /* IN - pointer to start of Bit32 */
   int        intsize)     /* IN - size of *intp */
{
/*
 * ASN.1 integer ::= 0x02 asnlength byte {byte}*
 */

   Bit32 intval;
   Bit32 mask;
   int add_null_byte = 0;
   Bit8 c;  Bit16 s;  /* UA: Handle 1 and 2-byte integers */

   if (intsize == sizeof(intval)) intval = (Bit32)*intp;
   else if (intsize == sizeof(c)) {
      c = *((char far *)intp);  intval = (Bit32)c;
      }
   else if (intsize == sizeof(s)) {
      s = *((short far *)intp);  intval = (Bit32)s;
      }
   else return NULL;
#if TESTING
   scpos(0,24);
   printf("build_us_int(): type=%d, size=%d, intval=%lu\n",
      type,intsize, intval);
#endif
   intsize = sizeof(intval);
   mask = 0xFFL << (8 * (sizeof(intval) - 1));
   /* mask is 0xFF000000 on a 32-bit machine */
   if ((u_char)((intval & mask) >> (8 * (sizeof(intval) - 1))) & 0x80) {
      /* if MSB is set */
      add_null_byte = 1;
      intsize++;
      }
   /*
    * Truncate "unnecessary" bytes off of the most significant end of this
    * 2's complement integer.  There should be no sequence of 9 consecutive
    * 1's or 0's at the most significant end of the integer.
    */
   mask = 0x1FFL << ((8 * (sizeof(intval) - 1)) - 1);
   /* mask is 0xFF800000 on a 32-bit machine */
   while ((((intval & mask) == 0) || ((intval & mask) == mask))
         && intsize > 1) {
      intsize--;
      intval <<= 8;
      }
   data = asn_build_header(data, datalength, type, intsize);
   if (data == NULL)
      return NULL;
   if (*datalength < intsize)
      return NULL;
   *datalength -= intsize;
   if (add_null_byte == 1) {
      *data++ = '\0';
      intsize--;
      }
   mask = 0xFFL << (8 * (sizeof(intval) - 1));
   /* mask is 0xFF000000 on a 32-bit machine */
   while(intsize--) {
      *data++ = (u_char)((intval & mask) >> (8 * (sizeof(intval) - 1)));
      intval <<= 8;
      }
   return data;
   }


/*
 * asn_parse_string - pulls an octet string out of an ASN octet string type.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the beginning of the next object.
 *
 *  "string" is filled with the octet string.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns NULL on any error.
 */
u_char *asn_parse_string(
   u_char *data,         /* IN - pointer to start of object */
   int    *datalength,   /* IN/OUT - number of valid bytes left in buffer */
   u_char *type,         /* OUT - asn type of object */
   u_char far *string,   /* IN/OUT - pointer to start of output buffer */
   int    *strlength)    /* IN/OUT - size of output buffer */
{
/*
 * ASN.1 octet string ::= primstring | cmpdstring
 * primstring ::= 0x04 asnlength byte {byte}*
 * cmpdstring ::= 0x24 asnlength string {string}*
 */
   u_char *bufp = data;
   Bit32 asn_length;

   *type = *bufp++;
   bufp = asn_parse_length(bufp, &asn_length);
   if (bufp == NULL)
      return NULL;
   if (asn_length + (bufp - data) > *datalength) {
      ERROR("overflow of message");
      return NULL;
      }
   if (asn_length > *strlength) {
      ERROR("string too long");
#if TESTING
      log_msg(LOG_INFO, 0, 
         "asn_parse_string(): asn_length=%d, *strlength=%d",
         asn_length, *strlength);
#endif
      return NULL;
      }
   farmove((char far *)string, (char far *)bufp, (int)asn_length);
   *strlength = (int)asn_length;
   *datalength -= (int)asn_length + (bufp - data);
   return bufp + asn_length;
   }


/*
 * asn_build_string - Builds an ASN octet string object containing the input string.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the beginning of the next object.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns NULL on any error.
 */
u_char *asn_build_string(
   u_char    *data,	   /* IN - pointer to start of object */
   int       *datalength,  /* IN/OUT - number of valid bytes left in buffer */
   u_char    type,	   /* IN - ASN type of string */
   u_char far *string,	   /* IN - pointer to start of input buffer */
   int       strlength)	   /* IN - size of input buffer */
{
/*
 * ASN.1 octet string ::= primstring | cmpdstring
 * primstring ::= 0x04 asnlength byte {byte}*
 * cmpdstring ::= 0x24 asnlength string {string}*
 * This code will never send a compound string.
 */
   data = asn_build_header(data, datalength, type, strlength);
   if (data == NULL)
      return NULL;
   if (*datalength < strlength)
      return NULL;
   farmove((char far *)data, (char far *)string, strlength);
   *datalength -= strlength;
   return data + strlength;
   }


/*
 * asn_parse_header - interprets the ID and length of the current object.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   in this object following the id and length.
 *
 *  Returns a pointer to the first byte of the contents of this object.
 *  Returns NULL on any error.
 */
u_char *asn_parse_header(
   u_char  *data, 	 /* IN - pointer to start of object */
   int	   *datalength,  /* IN/OUT - number of valid bytes left in buffer */
   u_char  *type)	 /* OUT - ASN type of object */
{
   u_char *bufp = data;
   int header_len;
   Bit32 asn_length;

#if TESTING
   printf("asn_parse_header(1): header OK.  data=%p, *datalength=%d\n",
      data, *datalength);
#endif
   /* This only works on data types < 30, i.e. no extension octets */
   if (IS_EXTENSION_ID(*bufp)) {
      ERROR("process ID >= 30");
      return NULL;
      }
   *type = *bufp;
   bufp = asn_parse_length(bufp + 1, &asn_length);
#if TESTING
   printf("asn_parse_header(2): header OK.  data=%p, bufp=%p, asn_length=%ld, *datalength=%d\n",
      data, bufp, asn_length, *datalength);
#endif
   if (bufp == NULL)
      return NULL;
   header_len = bufp - data;
   if (header_len + asn_length > *datalength) {
      ERROR("asn length too long");
      return NULL;
      }
   *datalength = (int)asn_length;
   return bufp;
   }

/*
 * asn_build_header - builds an ASN header for an object with the ID and
 * length specified.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   in this object following the id and length.
 *
 *  This only works on data types < 30, i.e. no extension octets.
 *  The maximum length is 0xFFFF;
 *
 *  Returns a pointer to the first byte of the contents of this object.
 *  Returns NULL on any error.
 */
u_char *asn_build_header(
   u_char *data,	/* IN - pointer to start of object */
   int	  *datalength,  /* IN/OUT - number of valid bytes left in buffer */
   u_char type, 	/* IN - ASN type of object */
   int	  length)	/* IN - length of object */
{
   u_char *new_data;
   if (*datalength < 1) {
/*      ERROR("< 1 byte for header");  ntm */
      return NULL;
      }
   *data++ = type;
   (*datalength)--;
   new_data = asn_build_length(data, datalength, length);
   if (new_data == NULL) {
/*      ERROR("No room for header");  ntm */
      }
   return new_data;
   }

/*
 * asn_build_sequence - builds an ASN header for a sequence with the ID and
 * length specified.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   in this object following the id and length.
 *
 *  This only works on data types < 30, i.e. no extension octets.
 *  The maximum length is 0xFFFF;
 *
 *  Returns a pointer to the first byte of the contents of this object.
 *  Returns NULL on any error.
 */
u_char *asn_build_sequence(
   u_char *data,	/* IN - pointer to start of object */
   int    *datalength,  /* IN/OUT - number of valid bytes left in buffer */
   u_char type, 	/* IN - ASN type of object */
   int    length)	/* IN - length of object */
{
   *datalength -= 4;
   if (*datalength < 0) {
      *datalength += 4;	/* Fix up before punting */
      return NULL;
      }
   *data++ = type;
   *data++ = (u_char)(0x02 | ASN_LONG_LEN);
   *data++ = (u_char)((length >> 8) & 0xFF);
   *data++ = (u_char)(length & 0xFF);
   return data;
   }

/*
 * asn_parse_length - interprets the length of the current object.
 *  On exit, length contains the value of this length field.
 *
 *  Returns a pointer to the first byte after this length
 *  field (aka: the start of the data field).
 *  Returns NULL on any error.
 */
u_char *asn_parse_length(
   u_char  *data,	/* IN - pointer to start of length field */
   Bit32   *length)	/* OUT - value of length field */
{
   u_char lengthbyte = *data;
   u_char k, *dp;  Bit32 v;  /* UA */

   if (lengthbyte & ASN_LONG_LEN) {
      lengthbyte &= ~ASN_LONG_LEN;	/* Turn MSb off */
      if (lengthbyte == 0) {
         ERROR("indefinite length");
	 return NULL;
	 }
      if (lengthbyte > sizeof(Bit32)) {
	 ERROR("data length > 4");
	 return NULL;
	 }
      v = 0;  dp = data+1;  /* UA */
      if (*dp & 0x80) {
         ERROR("negative length");
	 return NULL;
	 }
      for (k = lengthbyte; k != 0; --k) v = v << 8 | *dp++;
      *length = v;
      return data + lengthbyte + 1;
      }
   else {  /* Short asnlength */
      if (lengthbyte & 0x80) {
         ERROR("negative length");
	 return NULL;
	 }
      *length = (Bit32)lengthbyte;
      return data + 1;
      }
   }

u_char *asn_build_length(
   u_char *data,	/* IN - pointer to start of object */
   int    *datalength,  /* IN/OUT - number of valid bytes left in buffer */
   int    length)	/* IN - length of object */
{
   u_char    *start_data = data;

   /* No indefinite lengths sent */
   if (length < 0x80) {
      if (*datalength < 1) {
/*	 ERROR("build_length");  ntm */
	 return NULL;
	 }	    
      *data++ = (u_char)length;
      }
   else if (length <= 0xFF) {
      if (*datalength < 2) {
/*	 ERROR("build_length");  ntm */
	 return NULL;
	 }	    
      *data++ = (u_char)(0x01 | ASN_LONG_LEN);
      *data++ = (u_char)length;
      }
   else { /* 0xFF < length <= 0xFFFF */
      if (*datalength < 3) {
/*	 ERROR("build_length");  ntm */
	 return NULL;
	 }	    
      *data++ = (u_char)(0x02 | ASN_LONG_LEN);
      *data++ = (u_char)((length >> 8) & 0xFF);
      *data++ = (u_char)(length & 0xFF);
      }
   *datalength -= (data - start_data);
   return data;
   }

/*
 * asn_parse_objid - pulls an object indentifier out of an ASN object identifier type.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the beginning of the next object.
 *
 *  "objid" is filled with the object identifier.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns NULL on any error.
 */
u_char *asn_parse_objid(
   u_char  *data,	 /* IN - pointer to start of object */
   int     *datalength,  /* IN/OUT - number of valid bytes left in buffer */
   u_char  *type,	 /* OUT - ASN type of object */
   oid	   *objid,	 /* IN/OUT - pointer to start of output buffer */
   int     *objidlength) /* IN/OUT - number of sub-id's in objid */
{
/*
 * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}*
 * subidentifier ::= {leadingbyte}* lastbyte
 * leadingbyte ::= 1 7bitvalue
 * lastbyte ::= 0 7bitvalue
 */
   u_char *bufp = data;
   oid *oidp = objid + 1;
   Bit32 subidentifier;
   Bit32 length, asn_length;

   *type = *bufp++;
   bufp = asn_parse_length(bufp, &asn_length);
   if (bufp == NULL)
      return NULL;
   if (asn_length + (bufp - data) > *datalength) {
      ERROR("overflow of message");
      return NULL;
      }
   *datalength -= (int)asn_length + (bufp - data);

   /* Handle invalid object identifier encodings of the form 06 00 robustly */
   if (asn_length == 0)
      objid[0] = objid[1] = 0;

   length = asn_length;
   (*objidlength)--;	/* Account for expansion of first byte */
   while (length > 0 && (*objidlength)-- > 0) {
      subidentifier = 0;
      do {	/* shift and add in low order 7 bits */
	 subidentifier = (subidentifier << 7) + (*(u_char *)bufp & ~ASN_BIT8);
	 length--;
	 } while (*(u_char *)bufp++ & ASN_BIT8); /* Last byte has high bit clear */
      if (subidentifier > MAX_SUBID) {
	 ERROR("subidentifier too long");
	 return NULL;
	 }
      *oidp++ = (oid)subidentifier;
      }

   /*
    * The first two subidentifiers are encoded into the first component
    * with the value (X * 40) + Y, where:
    *  X is the value of the first subidentifier.
    *  Y is the value of the second subidentifier.
    */
   subidentifier = (Bit32)objid[1];
   if (subidentifier == 0x2B) {
      objid[0] = 1;
      objid[1] = 3;
      }
   else {
      objid[1] = (u_char)(subidentifier % 40);
      objid[0] = (u_char)((subidentifier - objid[1]) / 40);
      }
   *objidlength = (int)(oidp - objid);
   return bufp;
   }

/*
 * asn_build_objid - Builds an ASN object identifier object containing the
 * input string.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the beginning of the next object.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns NULL on any error.
 */
u_char *asn_build_objid(
   u_char *data,	 /* IN - pointer to start of object */
   int	  *datalength,   /* IN/OUT - number of valid bytes left in buffer */
   u_char  type,	 /* IN - ASN type of object */
   oid far *objid,	 /* IN - pointer to start of input buffer */
   int	   objidlength)  /* IN - number of sub-id's in objid */
{
/*
 * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}*
 * subidentifier ::= {leadingbyte}* lastbyte
 * leadingbyte ::= 1 7bitvalue
 * lastbyte ::= 0 7bitvalue
 */
   u_char buf[MAX_OID_LEN];
   u_char *bp = buf;
   oid far *op = objid;
   int    asnlength;
   Bit32 subid, mask, testmask;
   int bits, testbits;

   if (objidlength < 2) {
      *bp++ = 0;
      objidlength = 0;
      }
   else {
      *bp++ = op[1] + (op[0] * 40);
      objidlength -= 2;
      op += 2;
      }

   while(objidlength-- > 0) {
      subid = *op++;
      if (subid < 128) {  /* OIDs are encoded base-127 */
	 *bp++ = subid;
         }
      else {
	 mask = 0x7F;  bits = 0;
	 /* testmask *MUST* !!!! be of an unsigned type */
	 for (testmask = 0x7F, testbits = 0; testmask != 0;
	       testmask <<= 7, testbits += 7) {
	    if (subid & testmask) {  /* If any bits set */
	       mask = testmask;
	       bits = testbits;
	       }
	    }
	 /* Mask can't be zero here */
         *bp++ = (u_char)(((subid & mask) >> bits) | ASN_BIT8);
	 bits -= 7;  mask = 0x7F << bits;  /* Nevil, 4 Sep 97 */
	 for ( ; mask != 0x7F; mask >>= 7, bits -= 7) {
	    *bp++ = (u_char)(((subid & mask) >> bits) | ASN_BIT8);
	    }
	 *bp++ = (u_char)(subid & mask);
         }
      }
   asnlength = bp - buf;
   data = asn_build_header(data, datalength, type, asnlength);
   if (data == NULL)
      return NULL;
   if (*datalength < asnlength)
      return NULL;
   farmove((char far *)data, (char far *)buf, asnlength);
   *datalength -= asnlength;
   return data + asnlength;
   }

/*
 * asn_parse_null - Interprets an ASN null type.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the beginning of the next object.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns NULL on any error.
 */
u_char *asn_parse_null(
   u_char  *data,	 /* IN - pointer to start of object */
   int	   *datalength,  /* IN/OUT - number of valid bytes left in buffer */
   u_char  *type)	 /* OUT - ASN type of object */
{
/*
 * ASN.1 null ::= 0x05 0x00
 */
   u_char *bufp = data;
   Bit32 asn_length;

   *type = *bufp++;
   bufp = asn_parse_length(bufp, &asn_length);
   if (bufp == NULL)
      return NULL;
   if (asn_length != 0) {
      ERROR("Malformed NULL");
      return NULL;
      }
   *datalength -= (bufp - data);
   return bufp + asn_length;
   }


/*
 * asn_build_null - Builds an ASN null object.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the beginning of the next object.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns NULL on any error.
 */
u_char *asn_build_null(
   u_char  *data,	 /* IN - pointer to start of object */
   int	   *datalength,  /* IN/OUT - number of valid bytes left in buffer */
   u_char  type)	 /* IN - ASN type of object */
{
/*
 * ASN.1 null ::= 0x05 0x00
 */
   return asn_build_header(data, datalength, type, 0);
   }

/*
 * asn_parse_bitstring - pulls a bitstring out of an ASN bitstring type.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the beginning of the next object.
 *
 *  "string" is filled with the bit string.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns NULL on any error.
 */
u_char *asn_parse_bitstring(
   u_char     *data,	   /* IN - pointer to start of object */
   int        *datalength, /* IN/OUT - nbr of valid bytes left in buffer */
   u_char     *type,	   /* OUT - asn type of object */
   u_char far *string,	   /* IN/OUT - pointer to start of output buffer */
   int        *strlength)  /* IN/OUT - size of output buffer */
{
/*
 * bitstring ::= 0x03 asnlength unused {byte}*
 *
 *               Note that the caller must set the first byte to
 *                  indicate the number of unused bits in the last byte,
 *                  and must set those unused bits to zero.
 */
   u_char *bufp = data;
   Bit32 asn_length;

   *type = *bufp++;
   bufp = asn_parse_length(bufp, &asn_length);
   if (bufp == NULL)
      return NULL;
   if (asn_length + (bufp - data) > *datalength) {
      ERROR("overflow of message");
      return NULL;
      }
   if (asn_length > *strlength) {
      ERROR("bitstring too long");
      return NULL;
      }
   if (asn_length < 1) {
      ERROR("Invalid bitstring");
      return NULL;
      }
   if (*bufp > 7) {  /* Unused bits in last byte */
      ERROR("Invalid bitstring");
      return NULL;
      }
   farmove((char far *)string, (char far *)bufp, (int)asn_length);
   *strlength = (int)asn_length;
   *datalength -= (int)asn_length + (bufp - data);
   return bufp + asn_length;
   }


/*
 * asn_build_bitstring - Builds an ASN bit string object containing the
 * input string.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the beginning of the next object.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns NULL on any error.
 */
u_char *asn_build_bitstring(
   u_char *data,	/* IN - pointer to start of object */
   int    *datalength,  /* IN/OUT - number of valid bytes left in buffer */
   u_char type, 	/* IN - ASN type of string */
   u_char far *string,	/* IN - pointer to start of input buffer */
   int    strlength)	/* IN - size of input buffer */
{
/*
 * ASN.1 bit string ::= 0x03 asnlength unused {byte}*
 */
   if (strlength < 1 || *string > 7) {  /* First byte = unused trailing bits */
      ERROR("Building invalid bitstring");
      return NULL;
      }
   data = asn_build_header(data, datalength, type, strlength);
   if (data == NULL)
      return NULL;
   if (*datalength < strlength)
      return NULL;
   farmove((char far *)string, (char far *)data, (int)strlength);
   *datalength -= strlength;
   return data + strlength;
   }


/*
 * asn_parse_unsigned_int64 - pulls a 64 bit unsigned long out of an ASN int
 * type.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the end of this object.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns NULL on any error.
 */
u_char *asn_parse_unsigned_int64(
   u_char  *data,	     /* IN - pointer to start of object */
   int     *datalength,      /* IN/OUT - nbr of valid bytes left in buffer */
   u_char  *type,            /* OUT - asn type of object */
   counter64 far *cp,        /* IN/OUT -pointer to counter struct */
   int     countersize)      /* IN - size of output buffer */
{
/*
 * ASN.1 integer ::= 0x02 asnlength byte {byte}*
 */
   u_char *bufp = data;
   Bit32 asn_length;
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
   counter64 low = 0;
#else
   unsigned long high = 0;
   unsigned long low = 0;
#endif
   if (countersize != sizeof(counter64)) {
      ERROR("not right size");
      return NULL;
     }

   *type = *bufp++;
   bufp = asn_parse_length(bufp, &asn_length);
   if (bufp == NULL) {
      ERROR("bad length");
      return NULL;
      }
   if (asn_length + (bufp - data) > *datalength) {
      ERROR("overflow of message");
      return NULL;
      }
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
   if ((asn_length > (sizeof(low) + 1)) ||
         ((asn_length == sizeof(low) + 1) && *bufp != 0x00)) {
#else
   if ((asn_length > (sizeof(low)+sizeof(high) + 1)) ||
         ((asn_length == (sizeof(low)+sizeof(high)) + 1) && *bufp != 0x00)) {
#endif
      ERROR("counter too large");
      return NULL;
      }
   *datalength -= (int)asn_length + (bufp - data);
   if (*bufp & 0x80) {
      low = -1; /* integer is negative */
#if SIZEOF_LONG_LONG != 8 && SIZEOF_LONG != 8
      high = -1;
#endif
      }
   while(asn_length--) {
#if SIZEOF_LONG_LONG != 8 && SIZEOF_LONG != 8
      high = (high << 8) | ((low & 0xFF000000) >> 24);
#endif
      low = (low << 8) | *bufp++;
      }
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
   *cp = low;
#else
   cp->low = low;
   cp->high = high;
#endif
   return bufp;
   }


/*
 * asn_build_unsigned_int64 - builds an ASN object containing a 64 bit integer.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the end of this object.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns NULL on any error.
 */
u_char *asn_build_unsigned_int64(
   u_char  *data,	     /* IN - pointer to start of output buffer */
   int     *datalength,      /* IN/OUT - nbr of valid bytes left in buffer */
   u_char  type,	     /* IN - asn type of object */
   counter64 far *cp,        /* IN - pointer to counter struct */
   int     countersize)      /* IN - size of *intp */
{
/*
 * ASN.1 integer ::= 0x02 asnlength byte {byte}*
 */

#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
   counter64 low, mask, mask2;
#else
   unsigned long low, mask, mask2;
   unsigned long high;
#endif
   int add_null_byte = 0;
   int intsize;

   if (countersize != sizeof(counter64))
      return NULL;
   intsize = sizeof(counter64);
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
   low = *cp;
#else
   low = cp->low;
   high = cp->high;
#endif
   /* mask is 0xFF000000 on a 32-bit machine */
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
   mask = (counter64)0xFF << (8 * (sizeof(low) - 1));
   if ((u_char)((low & mask) >> (8 * (sizeof(low) - 1))) & 0x80) {
#else
   mask = (unsigned long)0xFF << (8 * (sizeof(high) - 1));
   if ((u_char)((high & mask) >> (8 * (sizeof(high) - 1))) & 0x80) {
#endif
      /* if MSB is set */
      add_null_byte = 1;
      intsize++;
      }
   /*
    * Truncate "unnecessary" bytes off of the most significant end of this
    * 2's complement integer.
    * There should be no sequence of 9 consecutive 1's or 0's at the most
    * significant end of the integer.
    */
   /* mask2 is 0xFF800000 on a 32-bit machine */
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
   mask2 = (counter64)0x1FF << ((8 * (sizeof(low) - 1)) - 1);
   while ((((low & mask2) == 0) || ((low & mask2) == mask2))
      /* mask changed to mask2.  Jean-Francois Pirus, 30 Apr 1998 */
         && intsize > 1) {
#else
   mask2 = (unsigned long)0x1FF << ((8 * (sizeof(high) - 1)) - 1);
   while((((high & mask2) == 0) || ((high & mask2) == mask2))
         && intsize > 1) {
      high = (high << 8)
	 | ((low & mask) >> (8 * (sizeof(low) - 1)));
#endif
      intsize--;
      low <<= 8;
      }
   data = asn_build_header(data, datalength, type, intsize);
   if (data == NULL)
      return NULL;
   if (*datalength < intsize)
      return NULL;
   *datalength -= intsize;
   if (add_null_byte == 1) {
      *data++ = '\0';
      intsize--;
      }
   while(intsize--) {
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
      *data++ = (u_char)((low & mask) >> (8 * (sizeof(low) - 1)));
#else
      *data++ = (u_char)((high & mask) >> (8 * (sizeof(high) - 1)));
      high = (high << 8)
         | ((low & mask) >> (8 * (sizeof(low) - 1)));
#endif
      low <<= 8;
      }
   return data;
   }
