/* ***************************************************************** *
 * Copyright 1998 International Business Machines Corporation. All   *
 * Rights Reserved.                                                  *
 *                                                                   *
 * Please read this carefully.  Your use of this reference           *
 * implementation of certain of the IETF public-key infrastructure   *
 * specifications ("Software") indicates your acceptance of the      *
 * following.  If you do not agree to the following, do not install  *
 * or use any of the Software.                                       *
 *                                                                   *
 * Permission to use, reproduce, distribute and create derivative    *
 * works from the Software ("Software Derivative Works"), and to     *
 * distribute such Software Derivative Works is hereby granted to    *
 * you by International Business Machines Corporation ("IBM").  This *
 * permission includes a license under the patents of IBM that are   *
 * necessarily infringed by your use of the Software as provided by  *
 * IBM.                                                              *
 *                                                                   *
 * IBM licenses the Software to you on an "AS IS" basis, without     *
 * warranty of any kind.  IBM HEREBY EXPRESSLY DISCLAIMS ALL         *
 * WARRANTIES OR CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING,   *
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS OF       *
 * MERCHANTABILITY, NON INFRINGEMENT AND FITNESS FOR A PARTICULAR    *
 * PURPOSE.  You are solely responsible for determining the          *
 * appropriateness of using this Software and assume all risks       *
 * associated with the use of this Software, including but not       *
 * limited to the risks of program errors, damage to or loss of      *
 * data, programs or equipment, and unavailability or interruption   *
 * of operations.                                                    *
 *                                                                   *
 * IBM WILL NOT BE LIABLE FOR ANY DIRECT DAMAGES OR FOR ANY SPECIAL, *
 * INCIDENTAL, OR  INDIRECT DAMAGES OR FOR ANY ECONOMIC              *
 * CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), EVEN   *
 * IF IBM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  IBM  *
 * will not be liable for the loss of, or damage to, your records or *
 * data, or any damages claimed by you based on a third party claim. *
 *                                                                   *
 * IBM wishes to obtain your feedback to assist in improving the     *
 * Software.  You grant IBM a world-wide, royalty-free right to use, *
 * copy, distribute, sublicense and prepare derivative works based   *
 * upon any feedback, including materials, error corrections,        *
 * Software Derivatives, enhancements, suggestions and the like that *
 * you provide to IBM relating to the Software (this does not        *
 * include products for which you charge a royalty and distribute to *
 * IBM under other terms and conditions).                            *
 *                                                                   *
 * You agree to distribute the Software and any Software Derivatives *
 * under a license agreement that: 1) is sufficient to notify all    *
 * licensees of the Software and Software Derivatives that IBM       *
 * assumes no liability for any claim that may arise regarding the   *
 * Software or Software Derivatives, and 2) that disclaims all       *
 * warranties, both express and implied, from IBM regarding the      *
 * Software and Software Derivatives.  (If you include this          *
 * Agreement with any distribution of the Software or Software       *
 * Derivatives you will have met this requirement.)  You agree that  *
 * you will not delete any copyright notices in the Software.        *
 *                                                                   *
 * This Agreement is the exclusive statement of your rights in the   *
 * Software as provided by IBM.   Except for the rights granted to   *
 * you in the second paragraph above, You are not granted any other  *
 * patent rights, including but not limited to the right to make     *
 * combinations of the Software with products that infringe IBM      *
 * patents. You agree to comply with all applicable laws and         *
 * regulations, including all export and import laws and regulation. *
 * This Agreement is governed by the laws of the State of New York.  *
 * This Agreement supersedes all other communications,               *
 * understandings or agreements we may have had prior to this        *
 * Agreement.                                                        *
 * ***************************************************************** */

#include "asn1.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

void asn_free_oid(asn_object_identifier o) {
  if (o==NULL) return;
  free(o->elements);
  o->elements = NULL;
  o->length = 0;
}

int asn_oid_equal(asn_object_identifier o1, 
                  asn_object_identifier o2)
{
  if (o1 == o2) return 1==1;
  if ((o1 == NULL) || (o2 == NULL)) return 0;
  if (o1->length != o2->length) return 0;
  return !memcmp(o1->elements, o2->elements, o1->length);
}


int AsnGetType(unsigned char ** p, 
               uint32 * len, 
               uint32 * ASNtype,
               int * constructed,
               int * ASNclass) {
// Given a length/pointer to an ASN1 octet stream, decodes
// the type field, and advances the length/pointer to the next
// element.  
  
  unsigned char * pp = *p;
  uint32 locLen = *len;
  if (*len == 0) return ASN_NO_MORE_DATA;

  *constructed = (*pp & 0x20u) != 0;
  *ASNclass = (*pp & 0xc0u) >> 6;

  if ((*pp & 0x1fu) != 0x1fu) {
    *ASNtype = *pp & 0x1fu;
    (*p)++;
    (*len)--;
    return 0;
  };
// Otherwise, high tag number
  p++;locLen--;
  *ASNtype = 0;
  do {
    if (locLen == 0) return ASN_NO_MORE_DATA;
    *ASNtype = *ASNtype << 7 + (*pp & 0x7fu);
    pp++; locLen--;
  } while (*pp & 0x80);
  *len = locLen;
  *p = pp;
  return 0;
}


int AsnGetLength(unsigned char ** p, 
                 uint32 * len,
                 int * definite,
                 uint32 * length) {
// Returns the length of the value.
  unsigned char * pp = *p;
  uint32 locLen = *len;
  uint32 lenLen;
  if (*len == 0) return ASN_NO_MORE_DATA;
  *definite = 1;
  if (*pp == 0x80u) {
    *definite = 0;
    *length = 0;
    (*len)--;
    (*p)++;
    return 0;
  }

  if ((*pp & 0x80u) == 0) {
    *length = *pp;
    (*len)--;
    (*p)++;
    return 0;
  };

  lenLen = *pp & 0x7fu;
  *length = 0;
  pp++; locLen--;
  while (lenLen > 0) {
    if (locLen == 0) return ASN_NO_MORE_DATA;
    *length = (*length << 8) + (uint32)*pp;
    pp++; locLen--; lenLen--;
  };
  *p = pp;
  *len = locLen;
  return 0;
}

int AsnGetIntegerValue(unsigned char ** p,
                       uint32 * len,
                       uint32 value_len,  
                       long * value) {
  unsigned char * pp = *p;
  uint32 locLen = *len;
  if (value_len > sizeof(long)) return ASN_VALUE_OVERFLOW;
  if (locLen < value_len) return ASN_NO_MORE_DATA;
  *value = 0;
  if (*pp & 0x80u) *value = -1;  // Pre-zero-extend negative values
  while (value_len > 0) {
    *value = *value << 8 + *pp;
    pp++;
    locLen--;
    value_len--;
  };
  *p = pp;
  *len = locLen;
  return 0;
}

int AsnGetIntegerValue(unsigned char ** p,
                       uint32 * len,
                       uint32 value_len,  
                       unsigned char ** value,
                       uint32 * octet_count,
                       int endian) {
  unsigned char * pp = *p;
  uint32 locLen = *len;
  if (value_len > sizeof(long)) return ASN_VALUE_OVERFLOW;
  if (value_len == 0) return ASN_INVALID_LENGTH;
  if (locLen < value_len) return ASN_NO_MORE_DATA;
  if ((*value = (unsigned char *)malloc(value_len)) == NULL)
    return ASN_NO_MEMORY;
  *octet_count = value_len;
  if (endian == ASN_LITTLE_ENDIAN) value+= value_len-1;
  while (value_len > 0) {
    if (endian == ASN_BIG_ENDIAN) *(*value)++ = *pp++;
    else *(*value)-- = *pp++;
    locLen--;
    value_len--;
  };
  *p = pp;
  *len = locLen;
  return 0;
}

int AsnGetLongIntegerValueBigEndian(unsigned char ** p,
                                    uint32 * len,
                                    uint32 value_len,
                                    unsigned char ** value,
                                    uint32 * octet_count) {
  return AsnGetIntegerValue(p,
                            len,
                            value_len,
                            value,
                            octet_count,
                            ASN_BIG_ENDIAN);
}

int AsnGetLongIntegerValueLittleEndian(unsigned char ** p,
                                       uint32 * len,
                                       uint32 value_len,
                                       unsigned char ** value,
                                       uint32 * octet_count) {
  return AsnGetIntegerValue(p,
                            len,
                            value_len,
                            value,
                            octet_count,
                            ASN_LITTLE_ENDIAN);
}

int AsnGetNullValue(unsigned char ** p,
                    uint32 * len,
                    uint32 value_len) {
  return 0;
}

int AsnGetBitstringValue(unsigned char ** p,
                         uint32 * len,
                         uint32 value_len,
                         unsigned char ** value,
                         uint32 * bit_count) {
  
  unsigned char * pp = *p;
  uint32 locLen = *len;
  uint32 unused_bits;
  if (value_len > sizeof(long)) return ASN_VALUE_OVERFLOW;
  if (value_len == 0) return ASN_INVALID_LENGTH;
  if (locLen < value_len) return ASN_NO_MORE_DATA;
  unused_bits = *pp++;
  if (unused_bits > 7) return ASN_INVALID_ENCODING;
  value_len--;
  if ((value_len == 0) && (unused_bits > 0)) 
    return ASN_INVALID_ENCODING;
  if ((*value = (unsigned char *)malloc(value_len)) == NULL)
    return ASN_NO_MEMORY;
  *bit_count = value_len * 8 - unused_bits;
  while (value_len > 0) {
    *(*value)++ = *pp++;
    locLen--;
    value_len--;
  };
  switch (unused_bits) {
  case 0: break;
  case 1: *(pp-1) = *(pp-1) & 0xfeu; break;
  case 2: *(pp-1) = *(pp-1) & 0xfcu; break;
  case 3: *(pp-1) = *(pp-1) & 0xf8u; break;
  case 4: *(pp-1) = *(pp-1) & 0xf0u; break;
  case 5: *(pp-1) = *(pp-1) & 0xe0u; break;
  case 6: *(pp-1) = *(pp-1) & 0xc0u; break;
  case 7: *(pp-1) = *(pp-1) & 0x80u; break;
  default: return ASN_INVALID_ENCODING;
  };
  *len = locLen;
  *p = pp;
  return 0;
}

int AsnGetOIDValue(unsigned char ** p,
                   uint32 * len,
                   uint32 value_len,
                   asn_object_identifier o) {
  unsigned char * pp = *p;
  uint32 locLen = *len;
  if (o == NULL) return ASN_INVALID_PARAMETER;
  if (value_len == 0) return ASN_INVALID_LENGTH;
  if (locLen < value_len) return ASN_NO_MORE_DATA;
  if ((o->elements = malloc(value_len)) == NULL) 
    return ASN_NO_MEMORY;
  o->length = value_len;
  memcpy(o->elements, pp, value_len);
  (*p) += value_len;
  (*len) -= value_len;
  return 0;
}

int AsnGetOctetstringValue(unsigned char ** p,
                           uint32 * len,
                           uint32 value_len,
                           unsigned char ** value,
                           uint32 * octet_count) {
  uint32 locLen = *len;
  if (value_len == 0) return ASN_INVALID_LENGTH;
  if (locLen < value_len) return ASN_NO_MORE_DATA;
  if ((*value = (unsigned char *)malloc(value_len)) = NULL)
    return ASN_NO_MEMORY;
  *octet_count = value_len;
  memcpy(*value, *p, value_len);
  *p=*p+value_len;
  *len=*len-value_len;
  return 0;
}

int AsnGetCharstringValue(unsigned char ** p,
                          uint32 * len,
                          uint32 value_len,
                          char * value) {

  uint32 locLen = *len;

  if (value_len == 0) return ASN_INVALID_LENGTH;
  if (locLen < value_len) return ASN_NO_MORE_DATA;
    
  if ((value = (char *)malloc(value_len + 1)) == NULL)
    return ASN_NO_MEMORY;
  strncpy(value, (char *)*p, value_len);
  (*p)[value_len] = '\0';
  
  (*p) += value_len;
  (*len) -= value_len;
  return 0;
}


int UtcstrToLocTime(char * utcTime, struct tm * tmTime) {
  int fields;
  unsigned years, months, days, hours, minutes, seconds;
  int tzo;
  int tzSign;
  long tzHours, tzMins;
  time_t utctime, mirrortime;
  
  fields = sscanf(utcTime, 
    "%2lu%2lu%2lu%2lu%2lu", 
    &years, 
    &months, 
    &days, 
    &hours, 
    &minutes);
  if (fields != 5) {
    return ASN_INVALID_ENCODING;
  };
// We may have seconds now, if the next character 
// isn't 'Z', '+' or '-'
  if ((utcTime[10] != 'Z') && 
      (utcTime[10] != '+') && 
      (utcTime[10] != '-')) {
  
    fields = sscanf(&utcTime[10],
      "%2lu",
      &seconds);
    if (fields != 1) {
      return ASN_INVALID_ENCODING;
    };
    tzo = 12;
  } else {
    seconds = 0;
    tzo = 10;
  };


  tzHours = 0;
  tzMins = 0;
  tzSign = 0;

  if (utcTime[tzo] != 'Z') {

    if (utcTime[tzo] == '+') {
      tzSign = +1;
    } else if (utcTime[tzo] == '-') {
      tzSign = -1;
    } else {
      return ASN_INVALID_ENCODING;
    };
    fields = sscanf(&utcTime[tzo+1],
      "%2lu%2lu",
      &tzHours,
      &tzMins);
    if (fields != 2) {
      return ASN_INVALID_ENCODING;
    };
  };

  hours = hours - tzHours * tzSign;
  minutes = minutes - tzMins * tzSign;

  tmTime->tm_sec = seconds;
  tmTime->tm_min = minutes;
  tmTime->tm_hour = hours;
  tmTime->tm_mday = days;
  tmTime->tm_mon = months - 1;
  tmTime->tm_year = years;
  tmTime->tm_wday = 0;
  tmTime->tm_yday = 0;
  tmTime->tm_isdst = 0; 

  utctime = mktime(tmTime);  
  mirrortime = mktime(gmtime(&utctime));
  utctime -= (mirrortime - utctime);
  memcpy(tmTime, localtime(&utctime), sizeof (struct tm));

  return 0;

}


