/**
  Free memory allocated by der_decode_sequence_flexi()
  @param in     The list to free
*/
void der_sequence_free(ltc_asn1_list *in)
{
    ltc_asn1_list *l;

    /* walk to the start of the chain */
    while (in->prev != NULL || in->parent != NULL) {
        if (in->parent != NULL) {
            in = in->parent;
        } else {
            in = in->prev;
        }
    }

    /* now walk the list and free stuff */
    while (in != NULL) {
        /* is there a child? */
        if (in->child) {
            /* disconnect */
            in->child->parent = NULL;
            der_sequence_free(in->child);
        }

        switch (in->type) {
        case LTC_ASN1_SET:
        case LTC_ASN1_SETOF:
        case LTC_ASN1_SEQUENCE:
            break;
        case LTC_ASN1_INTEGER :
            if (in->data != NULL) {
                mp_clear(in->data);
            }
            break;
        default               :
            if (in->data != NULL) {
                XFREE(in->data);
            }
        }

        /* move to next and free current */
        l = in->next;
        free(in);
        in = l;
    }
}
/** 
   ASN.1 DER Flexi(ble) decoder will decode arbitrary DER packets and create a linked list of the decoded elements.
   @param in      The input buffer
   @param inlen   [in/out] The length of the input buffer and on output the amount of decoded data 
   @param out     [out] A pointer to the linked list
   @return CRYPT_OK on success.
*/   
int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out)
{
   ltc_asn1_list *l;
   unsigned long err, type, len, totlen, x, y;
   void          *realloc_tmp;
   int           isConstructed;
   
   LTC_ARGCHK(in    != NULL);
   LTC_ARGCHK(inlen != NULL);
   LTC_ARGCHK(out   != NULL);

   l = NULL;
   totlen = 0;
   
   /* scan the input and and get lengths and what not */
   while (*inlen) {     
      /* read the type byte */
      type = *in;

      /* fetch length */
      len = fetch_length(in, *inlen);
      if (len > *inlen) {
         err = CRYPT_INVALID_PACKET;
         goto error;
      }

      /* alloc new link */
      if (l == NULL) {
         l = XCALLOC(1, sizeof(*l));
         if (l == NULL) {
            err = CRYPT_MEM;
            goto error;
         }
      } else {
         l->next = XCALLOC(1, sizeof(*l));
         if (l->next == NULL) {
            err = CRYPT_MEM;
            goto error;
         }
         l->next->prev = l;
         l = l->next;
      }

      if ((isConstructed = ((type & 0xE0) == 0xA0 ? 1 : 0))) {
         /* constructed, use the 'used' field to store the original tag number */
         l->used = (type & 0x1F);
         /* treat constructed elements like SETs */
         type = 0x31;
      }
     
     /* now switch on type */
      switch (type) {
         case 0x01: /* BOOLEAN */
            l->type = LTC_ASN1_BOOLEAN;
            l->size = 1;
            l->data = XCALLOC(1, sizeof(int));
       
            if ((err = der_decode_boolean(in, *inlen, l->data)) != CRYPT_OK) {
               goto error;
            }
        
            if ((err = der_length_boolean(&len)) != CRYPT_OK) {
               goto error;
            }
            break;

         case 0x02: /* INTEGER */
             /* init field */
             l->type = LTC_ASN1_INTEGER;
             l->size = 1;
             if ((err = mp_init(&l->data)) != CRYPT_OK) {
                 goto error;
             }
             
             /* decode field */
             if ((err = der_decode_integer(in, *inlen, l->data)) != CRYPT_OK) {
                 goto error;
             }
             
             /* calc length of object */
             if ((err = der_length_integer(l->data, &len)) != CRYPT_OK) {
                 goto error;
             }
             break;

         case 0x03: /* BIT */
            /* init field */
            l->type = LTC_ASN1_BIT_STRING;
            l->size = len * 8; /* *8 because we store decoded bits one per char and they are encoded 8 per char.  */

            if ((l->data = XCALLOC(1, l->size)) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }
            
            if ((err = der_decode_bit_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
               goto error;
            }
            
            if ((err = der_length_bit_string(l->size, &len)) != CRYPT_OK) {
               goto error;
            }
            break;

         case 0x04: /* OCTET */

            /* init field */
            l->type = LTC_ASN1_OCTET_STRING;
            l->size = len;

            if ((l->data = XCALLOC(1, l->size)) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }
            
            if ((err = der_decode_octet_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
               goto error;
            }
            
            if ((err = der_length_octet_string(l->size, &len)) != CRYPT_OK) {
               goto error;
            }
            break;

         case 0x05: /* NULL */
         
            /* valid NULL is 0x05 0x00 */
            if (in[0] != 0x05 || in[1] != 0x00) {
               err = CRYPT_INVALID_PACKET;
               goto error;
            }
            
            /* simple to store ;-) */
            l->type = LTC_ASN1_NULL;
            l->data = NULL;
            l->size = 0;
            len     = 2;
            
            break;
         
         case 0x06: /* OID */
         
            /* init field */
            l->type = LTC_ASN1_OBJECT_IDENTIFIER;
            l->size = len;

            if ((l->data = XCALLOC(len, sizeof(unsigned long))) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }
            
            if ((err = der_decode_object_identifier(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
               goto error;
            }
            
            if ((err = der_length_object_identifier(l->data, l->size, &len)) != CRYPT_OK) {
               goto error;
            }
            
            /* resize it to save a bunch of mem */
            if ((realloc_tmp = XREALLOC(l->data, l->size * sizeof(unsigned long))) == NULL) {
               /* out of heap but this is not an error */
               break;
            }
            l->data = realloc_tmp;
            break;
  
         case 0x0C: /* UTF8 */
         
            /* init field */
            l->type = LTC_ASN1_UTF8_STRING;
            l->size = len;

            if ((l->data = XCALLOC(sizeof(wchar_t), l->size)) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }
            
            if ((err = der_decode_utf8_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
               goto error;
            }
            
            if ((err = der_length_utf8_string(l->data, l->size, &len)) != CRYPT_OK) {
               goto error;
            }
            break;

         case 0x13: /* PRINTABLE */
         
            /* init field */
            l->type = LTC_ASN1_PRINTABLE_STRING;
            l->size = len;

            if ((l->data = XCALLOC(1, l->size)) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }
            
            if ((err = der_decode_printable_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
               goto error;
            }
            
            if ((err = der_length_printable_string(l->data, l->size, &len)) != CRYPT_OK) {
               goto error;
            }
            break;
         
         case 0x14: /* TELETEXT */
         
            /* init field */
            l->type = LTC_ASN1_TELETEX_STRING;
            l->size = len;

            if ((l->data = XCALLOC(1, l->size)) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }
            
            if ((err = der_decode_teletex_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
               goto error;
            }
            
            if ((err = der_length_teletex_string(l->data, l->size, &len)) != CRYPT_OK) {
               goto error;
            }
            break;
            
         case 0x16: /* IA5 */
         
            /* init field */
            l->type = LTC_ASN1_IA5_STRING;
            l->size = len;

            if ((l->data = XCALLOC(1, l->size)) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }
            
            if ((err = der_decode_ia5_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
               goto error;
            }
            
            if ((err = der_length_ia5_string(l->data, l->size, &len)) != CRYPT_OK) {
               goto error;
            }
            break;
         
         case 0x17: /* UTC TIME */
         
            /* init field */
            l->type = LTC_ASN1_UTCTIME;
            l->size = 1;

            if ((l->data = XCALLOC(1, sizeof(ltc_utctime))) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }
            
            len = *inlen;
            if ((err = der_decode_utctime(in, &len, l->data)) != CRYPT_OK) {
               goto error;
            }
            
            if ((err = der_length_utctime(l->data, &len)) != CRYPT_OK) {
               goto error;
            }
            break;
         
         case 0x30: /* SEQUENCE */
         case 0x31: /* SET */
         
             /* init field */
             l->type = (isConstructed ? LTC_ASN1_CONSTRUCTED : ((type == 0x30) ? LTC_ASN1_SEQUENCE : LTC_ASN1_SET));
             
             /* we have to decode the SEQUENCE header and get it's length */
             
                /* move past type */
                ++in; --(*inlen);
                
                /* read length byte */
                x = *in++; --(*inlen);
                
                /* smallest SEQUENCE/SET header */
                y = 2;
                
                /* now if it's > 127 the next bytes are the length of the length */
                if (x > 128) {
                   x      &= 0x7F;
                   in     += x;
                   *inlen -= x;
                   
                   /* update sequence header len */
                   y      += x;
                }
             
             /* Sequence elements go as child */
             len = len - y;
             if ((err = der_decode_sequence_flexi(in, &len, &(l->child))) != CRYPT_OK) {
                goto error;
             }
             
             /* len update */
             totlen += y;
             
             /* link them up y0 */
             l->child->parent = l;
             
             break;
         default:
           /* invalid byte ... this is a soft error */
           /* remove link */
           if (l->prev) {
              l       = l->prev;
              XFREE(l->next);
              l->next = NULL;
           }
           goto outside;
      }
      
      /* advance pointers */
      totlen  += len;
      in      += len;
      *inlen  -= len;
   }
   
outside:   

   /* rewind l please */
   while (l->prev != NULL || l->parent != NULL) {
      if (l->parent != NULL) {
         l = l->parent;
      } else {
         l = l->prev;
      }
   }
   
   /* return */
   *out   = l;
   *inlen = totlen;
   return CRYPT_OK;

error:
   /* free list */
   der_sequence_free(l);

   return err;
}
/**
   ASN.1 DER Flexi(ble) decoder will decode arbitrary DER packets and create a linked list of the decoded elements.
   @param in      The input buffer
   @param inlen   [in/out] The length of the input buffer and on output the amount of decoded data
   @param out     [out] A pointer to the linked list
   @return CRYPT_OK on success.
*/
int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out)
{
   ltc_asn1_list *l;
   unsigned long err, type, len, totlen, data_offset;
   void          *realloc_tmp;

   LTC_ARGCHK(in    != NULL);
   LTC_ARGCHK(inlen != NULL);
   LTC_ARGCHK(out   != NULL);

   l = NULL;
   totlen = 0;

   /* scan the input and and get lengths and what not */
   while (*inlen) {
      /* read the type byte */
      type = *in;

      /* fetch length */
      len = fetch_length(in, *inlen, &data_offset);
      if (len > *inlen) {
         err = CRYPT_INVALID_PACKET;
         goto error;
      }

      /* alloc new link */
      if (l == NULL) {
         l = XCALLOC(1, sizeof(*l));
         if (l == NULL) {
            err = CRYPT_MEM;
            goto error;
         }
      } else {
         l->next = XCALLOC(1, sizeof(*l));
         if (l->next == NULL) {
            err = CRYPT_MEM;
            goto error;
         }
         l->next->prev = l;
         l = l->next;
      }

      if ((type & 0x20) && (type != 0x30) && (type != 0x31)) {
         /* constructed, use the 'used' field to store the original identifier */
         l->used = type;
         /* treat constructed elements like SETs */
         type = 0x20;
      }
      else if ((type & 0xC0) == 0x80) {
         /* context-specific, use the 'used' field to store the original identifier */
         l->used = type;
         /* context-specific elements are treated as opaque data */
         type = 0x80;
      }

     /* now switch on type */
      switch (type) {
         case 0x01: /* BOOLEAN */
            l->type = LTC_ASN1_BOOLEAN;
            l->size = 1;
            l->data = XCALLOC(1, sizeof(int));

            if ((err = der_decode_boolean(in, *inlen, l->data)) != CRYPT_OK) {
               goto error;
            }

            if ((err = der_length_boolean(&len)) != CRYPT_OK) {
               goto error;
            }
            break;

         case 0x02: /* INTEGER */
             /* init field */
             l->type = LTC_ASN1_INTEGER;
             l->size = 1;
             if ((err = mp_init(&l->data)) != CRYPT_OK) {
                 goto error;
             }

             /* decode field */
             if ((err = der_decode_integer(in, *inlen, l->data)) != CRYPT_OK) {
                 goto error;
             }

             /* calc length of object */
             if ((err = der_length_integer(l->data, &len)) != CRYPT_OK) {
                 goto error;
             }
             break;

         case 0x03: /* BIT */
            /* init field */
            l->type = LTC_ASN1_BIT_STRING;
            l->size = len * 8; /* *8 because we store decoded bits one per char and they are encoded 8 per char.  */

            if ((l->data = XCALLOC(1, l->size)) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }

            if ((err = der_decode_bit_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
               goto error;
            }

            if ((err = der_length_bit_string(l->size, &len)) != CRYPT_OK) {
               goto error;
            }
            break;

         case 0x04: /* OCTET */

            /* init field */
            l->type = LTC_ASN1_OCTET_STRING;
            l->size = len;

            if ((l->data = XCALLOC(1, l->size)) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }

            if ((err = der_decode_octet_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
               goto error;
            }

            if ((err = der_length_octet_string(l->size, &len)) != CRYPT_OK) {
               goto error;
            }
            break;

         case 0x05: /* NULL */

            /* valid NULL is 0x05 0x00 */
            if (in[0] != 0x05 || in[1] != 0x00) {
               err = CRYPT_INVALID_PACKET;
               goto error;
            }

            /* simple to store ;-) */
            l->type = LTC_ASN1_NULL;
            l->data = NULL;
            l->size = 0;
            len     = 2;

            break;

         case 0x06: /* OID */

            /* init field */
            l->type = LTC_ASN1_OBJECT_IDENTIFIER;
            l->size = len;

            if ((l->data = XCALLOC(len, sizeof(unsigned long))) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }

            if ((err = der_decode_object_identifier(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
               goto error;
            }

            if ((err = der_length_object_identifier(l->data, l->size, &len)) != CRYPT_OK) {
               goto error;
            }

            /* resize it to save a bunch of mem */
            if ((realloc_tmp = XREALLOC(l->data, l->size * sizeof(unsigned long))) == NULL) {
               /* out of heap but this is not an error */
               break;
            }
            l->data = realloc_tmp;
            break;

         case 0x0C: /* UTF8 */

            /* init field */
            l->type = LTC_ASN1_UTF8_STRING;
            l->size = len;

            if ((l->data = XCALLOC(sizeof(wchar_t), l->size)) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }

            if ((err = der_decode_utf8_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
               goto error;
            }

            if ((err = der_length_utf8_string(l->data, l->size, &len)) != CRYPT_OK) {
               goto error;
            }
            break;

         case 0x13: /* PRINTABLE */

            /* init field */
            l->type = LTC_ASN1_PRINTABLE_STRING;
            l->size = len;

            if ((l->data = XCALLOC(1, l->size)) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }

            if ((err = der_decode_printable_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
               goto error;
            }

            if ((err = der_length_printable_string(l->data, l->size, &len)) != CRYPT_OK) {
               goto error;
            }
            break;

         case 0x14: /* TELETEXT */

            /* init field */
            l->type = LTC_ASN1_TELETEX_STRING;
            l->size = len;

            if ((l->data = XCALLOC(1, l->size)) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }

            if ((err = der_decode_teletex_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
               goto error;
            }

            if ((err = der_length_teletex_string(l->data, l->size, &len)) != CRYPT_OK) {
               goto error;
            }
            break;

         case 0x16: /* IA5 */

            /* init field */
            l->type = LTC_ASN1_IA5_STRING;
            l->size = len;

            if ((l->data = XCALLOC(1, l->size)) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }

            if ((err = der_decode_ia5_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
               goto error;
            }

            if ((err = der_length_ia5_string(l->data, l->size, &len)) != CRYPT_OK) {
               goto error;
            }
            break;

         case 0x17: /* UTC TIME */

            /* init field */
            l->type = LTC_ASN1_UTCTIME;
            l->size = 1;

            if ((l->data = XCALLOC(1, sizeof(ltc_utctime))) == NULL) {
               err = CRYPT_MEM;
               goto error;
            }

            len = *inlen;
            if ((err = der_decode_utctime(in, &len, l->data)) != CRYPT_OK) {
               goto error;
            }

            if ((err = der_length_utctime(l->data, &len)) != CRYPT_OK) {
               goto error;
            }
            break;

         case 0x20: /* Any CONSTRUCTED element that is neither SEQUENCE nor SET */
         case 0x30: /* SEQUENCE */
         case 0x31: /* SET */

             /* init field */
             if (type == 0x20) {
                l->type = LTC_ASN1_CONSTRUCTED;
             }
             else if (type == 0x30) {
                l->type = LTC_ASN1_SEQUENCE;
             }
             else {
                l->type = LTC_ASN1_SET;
             }

             /* jump to the start of the data */
             in     += data_offset;
             *inlen -= data_offset;
             len = len - data_offset;

             /* Sequence elements go as child */
             if ((err = der_decode_sequence_flexi(in, &len, &(l->child))) != CRYPT_OK) {
                goto error;
             }

             /* len update */
             totlen += data_offset;

             /* the flexi decoder can also do nothing, so make sure a child has been allocated */
             if (l->child) {
                /* link them up y0 */
                l->child->parent = l;
             }

             break;

         case 0x80: /* Context-specific */
             l->type = LTC_ASN1_CONTEXT_SPECIFIC;

             if ((l->data = XCALLOC(1, len - data_offset)) == NULL) {
                err = CRYPT_MEM;
                goto error;
             }

             XMEMCPY(l->data, in + data_offset, len - data_offset);
             l->size = len - data_offset;

             break;

         default:
           /* invalid byte ... this is a soft error */
           /* remove link */
           if (l->prev) {
              l       = l->prev;
              XFREE(l->next);
              l->next = NULL;
           }
           goto outside;
      }

      /* advance pointers */
      totlen  += len;
      in      += len;
      *inlen  -= len;
   }

outside:

   /* in case we processed anything */
   if (totlen) {
      /* rewind l please */
      while (l->prev != NULL || l->parent != NULL) {
         if (l->parent != NULL) {
            l = l->parent;
         } else {
            l = l->prev;
         }
      }
   }

   /* return */
   *out   = l;
   *inlen = totlen;
   return CRYPT_OK;

error:
   /* free list */
   der_sequence_free(l);

   return err;
}