/*
 * src/lib/krb5/asn.1/asn1_encode.c
 * 
 * Copyright 1994 by the Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * Export of this software from the United States of America may
 *   require a specific license from the United States Government.
 *   It is the responsibility of any person or organization contemplating
 *   export to obtain such a license before exporting.
 * 
 * WITHIN THAT CONSTRAINT, 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 M.I.T. not be used in advertising or publicity pertaining
 * to distribution of the software without specific, written prior
 * permission.  Furthermore if you modify this software you must label
 * your software as modified software and not distribute it in such a
 * fashion that it might be confused with the original M.I.T. software.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 */

/* ASN.1 primitive encoders */

#include "asn1_encode.h"
#include "asn1_make.h"

asn1_error_code asn1_encode_integer(buf, val, retlen)
     asn1buf * buf;
     const long val;
     int * retlen;
{
  asn1_error_code retval;
  int length = 0, partlen;
  long valcopy;
  int digit;
  
  valcopy = val;
  do {
    digit = (int) (valcopy&0xFF);
    retval = asn1buf_insert_octet(buf,(asn1_octet) digit);
    if(retval) return retval;
    length++;
    valcopy = valcopy >> 8;
  } while (valcopy != 0 && valcopy != ~0);

  if((val > 0) && ((digit&0x80) == 0x80)) { /* make sure the high bit is */
    retval = asn1buf_insert_octet(buf,0); /* of the proper signed-ness */
    if(retval) return retval;
    length++;
  }else if((val < 0) && ((digit&0x80) != 0x80)){
    retval = asn1buf_insert_octet(buf,0xFF);
    if(retval) return retval;
    length++;
  }

  retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_INTEGER,length, &partlen); 
  if(retval) return retval;
  length += partlen;

  *retlen = length;
  return 0;
}

asn1_error_code asn1_encode_unsigned_integer(buf, val, retlen)
     asn1buf * buf;
     const unsigned long val;
     int * retlen;
{
  asn1_error_code retval;
  int length = 0, partlen;
  unsigned long valcopy;
  int digit;
  
  valcopy = val;
  do {
    digit = (int) (valcopy&0xFF);
    retval = asn1buf_insert_octet(buf,(asn1_octet) digit);
    if(retval) return retval;
    length++;
    valcopy = valcopy >> 8;
  } while (valcopy != 0 && valcopy != ~0);

  if(digit&0x80) {		          /* make sure the high bit is */
    retval = asn1buf_insert_octet(buf,0); /* of the proper signed-ness */
    if(retval) return retval;
    length++;
  }

  retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_INTEGER,length, &partlen); 
  if(retval) return retval;
  length += partlen;

  *retlen = length;
  return 0;
}

asn1_error_code asn1_encode_octetstring(buf, len, val, retlen)
     asn1buf * buf;
     const int len;
     const asn1_octet * val;
     int * retlen;
{
  asn1_error_code retval;
  int length;

  retval = asn1buf_insert_octetstring(buf,len,val);
  if(retval) return retval;
  retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_OCTETSTRING,len,&length);
  if(retval) return retval;

  *retlen = len + length;
  return 0;
}

asn1_error_code asn1_encode_charstring(buf, len, val, retlen)
     asn1buf * buf;
     const int len;
     const char * val;
     int * retlen;
{
  asn1_error_code retval;
  int length;

  retval = asn1buf_insert_charstring(buf,len,val);
  if(retval) return retval;
  retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_OCTETSTRING,len,&length);
  if(retval) return retval;

  *retlen = len + length;
  return 0;
}

asn1_error_code asn1_encode_null(buf, retlen)
     asn1buf * buf;
     int * retlen;
{
  asn1_error_code retval;
  
  retval = asn1buf_insert_octet(buf,0x00);
  if(retval) return retval;
  retval = asn1buf_insert_octet(buf,0x05);
  if(retval) return retval;

  *retlen = 2;
  return 0;
}

asn1_error_code asn1_encode_printablestring(buf, len, val, retlen)
     asn1buf * buf;
     const int len;
     const char * val;
     int * retlen;
{
  asn1_error_code retval;
  int length;

  retval = asn1buf_insert_charstring(buf,len,val);
  if(retval) return retval;
  retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_PRINTABLESTRING,len,			 &length);
  if(retval) return retval;

  *retlen = len + length;
  return 0;
}

asn1_error_code asn1_encode_ia5string(buf, len, val, retlen)
     asn1buf * buf;
     const int len;
     const char * val;
     int * retlen;
{
  asn1_error_code retval;
  int length;

  retval = asn1buf_insert_charstring(buf,len,val);
  if(retval) return retval;
  retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_IA5STRING,len,			 &length);
  if(retval) return retval;

  *retlen = len + length;
  return 0;
}

#ifdef macintosh
#define EPOCH ((70 * 365 * 24 * 60 * 60) + (17 *  24 * 60 * 60) + (getTimeZoneOffset() * 60 * 60))
#else
#define EPOCH (0)
#endif

asn1_error_code asn1_encode_generaltime(buf, val, retlen)
     asn1buf * buf;
     const time_t val;
     int * retlen;
{
  asn1_error_code retval;
  struct tm *gtime;
  char s[16];
  int length, sum=0;
  time_t gmt_time;

  gmt_time = val + EPOCH;
  gtime = gmtime(&gmt_time);

  /*
   * Time encoding: YYYYMMDDhhmmssZ
   *
   * Sanity check this just to be paranoid, as gmtime can return NULL,
   * and some bogus implementations might overrun on the sprintf.
   */
  if (gtime == NULL ||
      gtime->tm_year > 8099 || gtime->tm_mon > 11 ||
      gtime->tm_mday > 31 || gtime->tm_hour > 23 ||
      gtime->tm_min > 59 || gtime->tm_sec > 59)
    return ASN1_BAD_GMTIME;
  sprintf(s, "%04d%02d%02d%02d%02d%02dZ",
	  1900+gtime->tm_year, gtime->tm_mon+1, gtime->tm_mday,
	  gtime->tm_hour, gtime->tm_min, gtime->tm_sec);

  retval = asn1buf_insert_charstring(buf,15,s);
  if(retval) return retval;
  sum = 15;

  retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_GENERALTIME,sum,&length);
  if(retval) return retval;
  sum += length;

  *retlen = sum;
  return 0;
}

asn1_error_code asn1_encode_generalstring(buf, len, val, retlen)
     asn1buf * buf;
     const int len;
     const char * val;
     int * retlen;
{
  asn1_error_code retval;
  int length;

  retval = asn1buf_insert_charstring(buf,len,val);
  if(retval) return retval;
  retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_GENERALSTRING,len,
			 &length);
  if(retval) return retval;

  *retlen = len + length;
  return 0;
}
