/*
 * Copyright (C) 2011-2015 ANSSI
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * \file
 *
 * \author Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>
 *
 */

/*
 * An ASN.1 Parser for DER-encoded structures.
 * This parser is not written to be complete or fast, but is rather
 * focused on stability and security.
 * It does not support all ASN.1 structure, only a meaningful subset
 * to decode x509v3 certificates (See RFC 3280).
 *
 * References (like 8.19.4) are relative to the ISO/IEC 8825-1:2003 document
 *
 */

#include "suricata-common.h"
#include "util-decode-der.h"
#include "util-validate.h"

#define MAX_OID_LENGTH 256

static Asn1Generic * DecodeAsn1DerBitstring(const unsigned char *buffer,
                                            uint32_t size, uint8_t depth,
                                            uint32_t *errcode) __attribute__((nonnull));
static Asn1Generic * DecodeAsn1DerBoolean(const unsigned char *buffer,
                                          uint32_t size, uint8_t depth,
                                          uint32_t *errcode) __attribute__((nonnull));
static Asn1Generic * DecodeAsn1DerIA5String(const unsigned char *buffer,
                                            uint32_t size, uint8_t depth,
                                            uint32_t *errcode) __attribute__((nonnull));
static Asn1Generic * DecodeAsn1DerInteger(const unsigned char *buffer,
                                          uint32_t size, uint8_t depth,
                                          uint32_t *errcode) __attribute__((nonnull));
static Asn1Generic * DecodeAsn1DerNull(const unsigned char *buffer,
                                       uint32_t size, uint8_t depth,
                                       uint32_t *errcode) __attribute__((nonnull));
static Asn1Generic * DecodeAsn1DerOctetString(const unsigned char *buffer,
                                              uint32_t size, uint8_t depth,
                                              uint32_t *errcode) __attribute__((nonnull));
static Asn1Generic * DecodeAsn1DerUTF8String(const unsigned char *buffer,
                                             uint32_t max_size, uint8_t depth,
                                             uint32_t *errcode) __attribute__((nonnull));
static Asn1Generic * DecodeAsn1DerOid(const unsigned char *buffer,
                                      uint32_t size, uint8_t depth,
                                      uint32_t *errcode) __attribute__((nonnull));
static Asn1Generic * DecodeAsn1DerPrintableString(const unsigned char *buffer,
                                                  uint32_t size, uint8_t depth,
                                                  uint32_t *errcode) __attribute__((nonnull));
static Asn1Generic * DecodeAsn1DerSequence(const unsigned char *buffer,
                                           uint32_t size, uint8_t depth,
                                           uint32_t *errcode) __attribute__((nonnull));
static Asn1Generic * DecodeAsn1DerSet(const unsigned char *buffer,
                                      uint32_t size, uint8_t depth,
                                      uint32_t *errcode) __attribute__((nonnull));
static Asn1Generic * DecodeAsn1DerT61String(const unsigned char *buffer,
                                            uint32_t size, uint8_t depth,
                                            uint32_t *errcode) __attribute__((nonnull));
static Asn1Generic * DecodeAsn1DerUTCTime(const unsigned char *buffer,
                                          uint32_t size, uint8_t depth,
                                          uint32_t *errcode) __attribute__((nonnull));
static Asn1Generic * DecodeAsn1DerGeneralizedTime(const unsigned char *buffer,
                                                  uint32_t size, uint8_t depth,
                                                  uint32_t *errcode) __attribute__((nonnull));
static Asn1Generic * DecodeAsn1DerGeneric(const unsigned char *buffer,
                                          uint32_t max_size, uint8_t depth,
                                          int seq_index, uint32_t *errcode) __attribute__((nonnull));

static Asn1Generic * Asn1GenericNew(void)
{
    Asn1Generic *obj;

    obj = SCMalloc(sizeof(Asn1Generic));
    if (obj != NULL)
        memset(obj, 0, sizeof(Asn1Generic));

    return obj;
}

/**
 * \retval r 0 ok, -1 error
 */
static int Asn1SequenceAppend(Asn1Generic *seq, Asn1Generic *node)
{
    Asn1Generic *it, *new_container;

    if (seq->data == NULL) {
        seq->data = node;
        return 0;
    }

    new_container = Asn1GenericNew();
    if (new_container == NULL)
        return -1;
    new_container->data = node;

    for (it=seq; it->next != NULL; it=it->next)
        ;

    it->next = new_container;
    return 0;
}

/* openssl has set a limit of 30, so stay close to that. */
#define DER_MAX_RECURSION_DEPTH 32

static Asn1Generic * DecodeAsn1DerGeneric(const unsigned char *buffer,
                                          uint32_t max_size, uint8_t depth,
                                          int seq_index, uint32_t *errcode)
{
    const unsigned char *d_ptr = buffer;
    uint32_t numbytes, el_max_size;
    Asn1ElementType el;
    uint8_t c;
    uint32_t i;
    Asn1Generic *child;
    uint8_t el_type;

    /* refuse excessive recursion */
    if (unlikely(depth >= DER_MAX_RECURSION_DEPTH)) {
        *errcode = ERR_DER_RECURSION_LIMIT;
        return NULL;
    }
    if (max_size < 2) {
        *errcode = ERR_DER_INVALID_SIZE;
        return NULL;
    }

    el.cls = (d_ptr[0] & 0xc0) >> 6;
    el.pc = (d_ptr[0] & 0x20) >> 5;
    el.tag = (d_ptr[0] & 0x1f);

    el_type = el.tag;

    if (el.tag == 0x1f) {
        *errcode = ERR_DER_INVALID_TAG;
        return NULL;
    }

    switch (el.cls) {
        case ASN1_CLASS_CONTEXTSPEC:
            /* get element type from definition, see:
               http://www.ietf.org/rfc/rfc3280.txt */
            if (depth == 2 && el.tag == 0) {
                /* TBSCertificate */
                el_type = ASN1_SEQUENCE;
                break;
            }
            if (depth == 2 && el.tag == 1) {
                /* issuerUniqueID */
                el_type = ASN1_BITSTRING;
                break;
            }
            if (depth == 2 && el.tag == 2) {
                /* subjectUniqueID */
                el_type = ASN1_BITSTRING;
                break;
            }
            if (depth == 2 && el.tag == 3) {
                /* extensions */
                el_type = ASN1_SEQUENCE;
                break;
            }
            /* unknown context specific value - do not decode */
            break;
    };

    el_max_size = max_size - (d_ptr-buffer);
    switch (el_type) {
        case ASN1_INTEGER:
            child = DecodeAsn1DerInteger(d_ptr, el_max_size, depth+1, errcode);
            break;
        case ASN1_BOOLEAN:
            child = DecodeAsn1DerBoolean(d_ptr, el_max_size, depth+1, errcode);
            break;
        case ASN1_NULL:
            child = DecodeAsn1DerNull(d_ptr, el_max_size, depth+1, errcode);
            break;
        case ASN1_BITSTRING:
            child = DecodeAsn1DerBitstring(d_ptr, el_max_size, depth+1,
                                           errcode);
            break;
        case ASN1_OID:
            child = DecodeAsn1DerOid(d_ptr, el_max_size, depth+1, errcode);
            break;
        case ASN1_IA5STRING:
            child = DecodeAsn1DerIA5String(d_ptr, el_max_size, depth+1,
                                           errcode);
            break;
        case ASN1_OCTETSTRING:
            child = DecodeAsn1DerOctetString(d_ptr, el_max_size, depth+1,
                                             errcode);
            break;
        case ASN1_UTF8STRING:
            child = DecodeAsn1DerUTF8String(d_ptr, el_max_size, depth+1,
                                            errcode);
            break;
        case ASN1_PRINTSTRING:
            child = DecodeAsn1DerPrintableString(d_ptr, el_max_size, depth+1,
                                                 errcode);
            break;
        case ASN1_SEQUENCE:
            child = DecodeAsn1DerSequence(d_ptr, el_max_size, depth+1, errcode);
            break;
        case ASN1_SET:
            child = DecodeAsn1DerSet(d_ptr, el_max_size, depth+1, errcode);
            break;
        case ASN1_T61STRING:
            child = DecodeAsn1DerT61String(d_ptr, el_max_size, depth+1,
                                           errcode);
            break;
        case ASN1_UTCTIME:
            child = DecodeAsn1DerUTCTime(d_ptr, el_max_size, depth+1, errcode);
            break;
        case ASN1_GENERALIZEDTIME:
            child = DecodeAsn1DerGeneralizedTime(d_ptr, el_max_size, depth+1, errcode);
            break;
        default:
            /* unknown ASN.1 type */
            child = NULL;
            child = Asn1GenericNew();
            if (child == NULL)
                break;

            child->type = el.tag;

            /* total sequence length */
            const unsigned char * save_d_ptr = d_ptr;
            d_ptr++;
            el_max_size--;
            c = d_ptr[0];

            /* short form 8.1.3.4 */
            if ((c & (1<<7))>>7 == 0) {
                child->length = c;
                d_ptr++;
            /* long form 8.1.3.5 */
            } else {
                numbytes = c & 0x7f;
                if (2 + numbytes > el_max_size) {
                    SCFree(child);
                    *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
                    return NULL;
                }
                child->length = 0;
                d_ptr++;
                for (i=0; i<numbytes; i++) {
                    child->length = child->length<<8 | d_ptr[0];
                    d_ptr++;
                }
            }

            /* fix the length for unknown objects, else sequence parsing
               will fail */
            child->length += (d_ptr - save_d_ptr);

            if (child->length > max_size - (d_ptr - buffer)) {
                *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
                SCFree(child);
                return NULL;
            }

            break;
    };

    if (child == NULL) {
        if (*errcode == 0)
            *errcode = ERR_DER_INVALID_OBJECT;
        return NULL;
    }

    /* child length should never be zero */
    if (child->length == 0) {
        *errcode = ERR_DER_INVALID_OBJECT;
        SCFree(child);
        return NULL;
    }

    child->header = el;
    return child;
}

static Asn1Generic * DecodeAsn1DerInteger(const unsigned char *buffer,
                                          uint32_t size, uint8_t depth,
                                          uint32_t *errcode)
{
    const unsigned char *d_ptr = buffer;
    uint8_t numbytes;
    uint32_t value;
    uint32_t i;
    Asn1Generic *a;

    numbytes = d_ptr[1];

    if ((uint32_t)(numbytes + 2) > size) {
        *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
        return NULL;
    }

    d_ptr += 2;

    value = 0;

    /* Here we need to ensure that numbytes is less than 4
       so integer affectation is possible. We set the value
       to 0xffffffff which is by convention the unknown value.
       In this case, the hexadecimal value must be used. */
    if (numbytes > 4) {
        value = 0xffffffff;
    } else {
        for (i=0; i<numbytes; i++) {
            value = value<<8 | d_ptr[i];
        }
    }

    a = Asn1GenericNew();
    if (a == NULL) {
        *errcode = ERR_DER_GENERIC;
        return NULL;
    }

    a->type = ASN1_INTEGER;
    a->length = (d_ptr - buffer) + numbytes;
    a->value = value;

    a->str = SCMalloc(2*numbytes + 1);
    if (a->str == NULL) {
        *errcode = ERR_DER_GENERIC;
        SCFree(a);
        return NULL;
    }

    for (i=0; i<numbytes; i++) {
        snprintf(a->str + 2*i, 2*(numbytes-i)+1, "%02X", d_ptr[i]);
    }
    a->str[2*numbytes]= '\0';

    return a;
}

static int DecodeAsn1BuildValue(const unsigned char **d_ptr, uint32_t *val,
                                uint8_t numbytes, uint32_t *errcode)
{
    int i;
    uint32_t value = 0;

    if (numbytes > 4) {
        *errcode = ERR_DER_INVALID_SIZE;

        /* too big won't fit: set it to 0xffffffff by convention */
        value = 0xffffffff;
        *val = value;
        return -1;
    } else {
        for (i=0; i<numbytes; i++) {
            value = value<<8 | (*d_ptr)[0];
            (*d_ptr)++;
        }
    }

    *val = value;
    return 0;
}

static Asn1Generic * DecodeAsn1DerBoolean(const unsigned char *buffer,
                                          uint32_t size, uint8_t depth,
                                          uint32_t *errcode)
{
    const unsigned char *d_ptr = buffer;
    uint8_t numbytes;
    uint32_t value;
    Asn1Generic *a;

    numbytes = d_ptr[1];
    if ((uint32_t)(numbytes + 2) > size) {
        *errcode = ERR_DER_INVALID_SIZE;
        return NULL;
    }
    d_ptr += 2;

    if (DecodeAsn1BuildValue(&d_ptr, &value, numbytes, errcode) == -1) {
        return NULL;
    }

    a = Asn1GenericNew();
    if (a == NULL) {
        *errcode = ERR_DER_GENERIC;
        return NULL;
    }

    a->type = ASN1_BOOLEAN;
    a->length = (d_ptr - buffer);
    a->value = value;

    return a;
}

static Asn1Generic * DecodeAsn1DerNull(const unsigned char *buffer,
                                       uint32_t size, uint8_t depth,
                                       uint32_t *errcode)
{
    const unsigned char *d_ptr = buffer;
    uint8_t numbytes;
    uint32_t value;
    Asn1Generic *a;

    numbytes = d_ptr[1];
    if ((uint32_t)(numbytes + 2) > size) {
        *errcode = ERR_DER_INVALID_SIZE;
        return NULL;
    }
    d_ptr += 2;

    if (DecodeAsn1BuildValue(&d_ptr, &value, numbytes, errcode) == -1) {
        return NULL;
    }

    a = Asn1GenericNew();
    if (a == NULL) {
        *errcode = ERR_DER_GENERIC;
        return NULL;
    }

    a->type = ASN1_NULL;
    a->length = (d_ptr - buffer);
    a->value = 0;

    return a;
}

static Asn1Generic * DecodeAsn1DerBitstring(const unsigned char *buffer,
                                            uint32_t max_size, uint8_t depth,
                                            uint32_t *errcode)
{
    const unsigned char *d_ptr = buffer;
    uint32_t length;
    uint8_t numbytes, c;
    Asn1Generic *a;

    d_ptr++;

    /* size */
    c = d_ptr[0];
    /* short form 8.1.3.4 */
    if ((c & (1<<7))>>7 == 0) {
        length = c;
        d_ptr++;
    /* long form 8.1.3.5 */
    } else {
        numbytes = c & 0x7f;
        if ((uint32_t)(numbytes + 2) > max_size) {
            *errcode = ERR_DER_INVALID_SIZE;
            return NULL;
        }
        d_ptr++;
        if (DecodeAsn1BuildValue(&d_ptr, &length, numbytes, errcode) == -1) {
            return NULL;
        }
    }

    if ((d_ptr-buffer) + length > max_size) {
        *errcode = ERR_DER_INVALID_SIZE;
        return NULL;
    }

    a = Asn1GenericNew();
    if (a == NULL) {
        *errcode = ERR_DER_GENERIC;
        return NULL;
    }

    a->type = ASN1_BITSTRING;
    a->strlen = length;

    a->str = SCMalloc(length);
    if (a->str == NULL) {
        *errcode = ERR_DER_GENERIC;
        SCFree(a);
        return NULL;
    }
    memcpy(a->str, (const char*)d_ptr, length);

    d_ptr += length;

    a->length = (d_ptr - buffer);
    return a;
}

static Asn1Generic * DecodeAsn1DerOid(const unsigned char *buffer,
                                      uint32_t max_size, uint8_t depth,
                                      uint32_t *errcode)
{
    const unsigned char *d_ptr = buffer;
    uint32_t oid_length, oid_value;
    uint8_t numbytes, c;
    Asn1Generic *a;
    uint32_t i;

    d_ptr++;

    /* size */
    c = d_ptr[0];
    /* short form 8.1.3.4 */
    if ((c & (1<<7))>>7 == 0) {
        oid_length = c;
        d_ptr++;
    /* long form 8.1.3.5 */
    } else {
        numbytes = c & 0x7f;
        if ((uint32_t)(numbytes + 2) > max_size) {
            *errcode = ERR_DER_INVALID_SIZE;
            return NULL;
        }
        d_ptr++;
        if (DecodeAsn1BuildValue(&d_ptr, &oid_length, numbytes, errcode) == -1) {
            return NULL;
        }
    }

    if (oid_length == 0 || (d_ptr-buffer) + oid_length > max_size) {
        *errcode = ERR_DER_INVALID_SIZE;
        return NULL;
    }

    a = Asn1GenericNew();
    if (a == NULL) {
        *errcode = ERR_DER_GENERIC;
        return NULL;
    }

    a->type = ASN1_OID;

    a->str = SCMalloc(MAX_OID_LENGTH);
    if (a->str == NULL) {
        *errcode = ERR_DER_GENERIC;
        SCFree(a);
        return NULL;
    }

    /* first element = X*40 + Y (See 8.19.4) */
    snprintf(a->str, MAX_OID_LENGTH, "%d.%d", (d_ptr[0]/40), (d_ptr[0]%40));
    d_ptr++;

    if (oid_length + (d_ptr-buffer) > max_size) {
        *errcode = ERR_DER_INVALID_SIZE;
        SCFree(a->str);
        SCFree(a);
        return NULL;
    }

    /* sub-identifiers are multi-valued, coded and 7 bits, first bit of
       the 8bits is used to indicate, if a new value is starting */
    for (i=1; i<oid_length; ) {
        int s = strlen(a->str);
        c = d_ptr[0];
        oid_value = 0;
        while ( i<oid_length && (c & (1<<7)) == 1<<7 ) {
            oid_value = oid_value<<7 | (c & ~(1<<7));
            d_ptr++;
            c = d_ptr[0];
            i++;
        }
        oid_value = oid_value<<7 | c;
        d_ptr++;
        i++;
        snprintf(a->str + s, MAX_OID_LENGTH - s, ".%d", oid_value);
    }

    a->length = (d_ptr - buffer);
    return a;
}

static Asn1Generic * DecodeAsn1DerIA5String(const unsigned char *buffer,
                                            uint32_t max_size, uint8_t depth,
                                            uint32_t *errcode)
{
    const unsigned char *d_ptr = buffer;
    uint32_t length, numbytes;
    Asn1Generic *a;
    unsigned char c;

    d_ptr++;
    max_size--;

    /* total sequence length */
    c = d_ptr[0];
    /* short form 8.1.3.4 */
    if ((c & (1<<7))>>7 == 0) {
        length = c;
        d_ptr++;
        max_size--;
    /* long form 8.1.3.5 */
    } else {
        numbytes = c & 0x7f;
        if (max_size < 1 + numbytes) {
            *errcode = ERR_DER_INVALID_SIZE;
            return NULL;
        }
        max_size -= 1 + numbytes;
        d_ptr++;
        if (DecodeAsn1BuildValue(&d_ptr, &length, numbytes, errcode) == -1) {
            return NULL;
        }
    }

    if (length == UINT32_MAX || (d_ptr-buffer) + length > max_size) {
        *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
        return NULL;
    }

    a = Asn1GenericNew();
    if (a == NULL) {
        *errcode = ERR_DER_GENERIC;
        return NULL;
    }

    a->type = ASN1_IA5STRING;
    a->strlen = length;

    a->str = SCMalloc(length+1);
    if (a->str == NULL) {
        *errcode = ERR_DER_GENERIC;
        SCFree(a);
        return NULL;
    }
    memcpy(a->str, (const char*)d_ptr, length);
    a->str[length] = 0;

    d_ptr += length;

    a->length = (d_ptr - buffer);
    return a;
}

static Asn1Generic * DecodeAsn1DerOctetString(const unsigned char *buffer,
                                              uint32_t max_size, uint8_t depth,
                                              uint32_t *errcode)
{
    const unsigned char *d_ptr = buffer;
    uint32_t length, numbytes;
    Asn1Generic *a;
    unsigned char c;

    d_ptr++;
    max_size--;

    /* total sequence length */
    c = d_ptr[0];
    /* short form 8.1.3.4 */
    if ((c & (1<<7))>>7 == 0) {
        length = c;
        d_ptr++;
        max_size--;
    /* long form 8.1.3.5 */
    } else {
        numbytes = c & 0x7f;
        if (max_size < 1 + numbytes) {
            *errcode = ERR_DER_INVALID_SIZE;
            return NULL;
        }
        max_size -= 1 + numbytes;
        d_ptr++;
        if (DecodeAsn1BuildValue(&d_ptr, &length, numbytes, errcode) == -1) {
            return NULL;
        }
    }

    if (length == UINT32_MAX || (d_ptr-buffer) + length > max_size) {
        *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
        return NULL;
    }
    a = Asn1GenericNew();
    if (a == NULL) {
        *errcode = ERR_DER_GENERIC;
        return NULL;
    }

    a->type = ASN1_OCTETSTRING;
    a->strlen = length;

    /* add one to the octet string for the 0. This will then allow us to
       use the string in printf */
    a->str = SCMalloc(length + 1);
    if (a->str == NULL) {
        *errcode = ERR_DER_GENERIC;
        SCFree(a);
        return NULL;
    }
    memcpy(a->str, (const char*)d_ptr, length);
    a->str[length] = 0;

    d_ptr += length;

    a->length = (d_ptr - buffer);
    return a;
}

static Asn1Generic * DecodeAsn1DerUTF8String(const unsigned char *buffer,
                                             uint32_t max_size, uint8_t depth,
                                             uint32_t *errcode)
{
    Asn1Generic *a = DecodeAsn1DerOctetString(buffer, max_size, depth, errcode);
    if (a != NULL)
        a->type = ASN1_UTF8STRING;

    return a;
}

static Asn1Generic * DecodeAsn1DerPrintableString(const unsigned char *buffer,
                                                  uint32_t max_size,
                                                  uint8_t depth,
                                                  uint32_t *errcode)
{
    const unsigned char *d_ptr = buffer;
    uint32_t length, numbytes;
    Asn1Generic *a;
    unsigned char c;

    d_ptr++;

    /* total sequence length */
    c = d_ptr[0];
    /* short form 8.1.3.4 */
    if ((c & (1<<7))>>7 == 0) {
        length = c;
        d_ptr++;
    /* long form 8.1.3.5 */
    } else {
        numbytes = c & 0x7f;
        d_ptr++;
        if (2 + numbytes > max_size) {
            *errcode = ERR_DER_INVALID_SIZE;
            return NULL;
        }
        if (DecodeAsn1BuildValue(&d_ptr, &length, numbytes, errcode) == -1) {
            return NULL;
        }
    }

    if (length == UINT32_MAX || (d_ptr-buffer) + length > max_size) {
        *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
        return NULL;
    }

    a = Asn1GenericNew();
    if (a == NULL) {
        *errcode = ERR_DER_GENERIC;
        return NULL;
    }

    a->type = ASN1_PRINTSTRING;
    a->strlen = length;

    a->str = SCMalloc(length+1);
    if (a->str == NULL) {
        *errcode = ERR_DER_GENERIC;
        SCFree(a);
        return NULL;
    }
    memcpy(a->str, (const char*)d_ptr, length);
    a->str[length] = 0;

    d_ptr += length;

    a->length = (d_ptr - buffer);
    return a;
}

static Asn1Generic * DecodeAsn1DerSequence(const unsigned char *buffer,
                                           uint32_t max_size, uint8_t depth,
                                           uint32_t *errcode)
{
    const unsigned char *d_ptr = buffer;
    uint32_t d_length, parsed_bytes, numbytes, el_max_size;
    uint8_t c;
    uint32_t seq_index;
    Asn1Generic *node;

    d_ptr++;

    node = Asn1GenericNew();
    if (node == NULL) {
        *errcode = ERR_DER_GENERIC;
        return NULL;
    }

    node->type = ASN1_SEQUENCE;

    /* total sequence length */
    c = d_ptr[0];
    /* short form 8.1.3.4 */
    if ((c & (1<<7))>>7 == 0) {
        d_length = c;
        d_ptr++;
    /* long form 8.1.3.5 */
    } else {
        numbytes = c & 0x7f;
        d_ptr++;
        if ( 2 + numbytes > max_size ) {
            *errcode = ERR_DER_INVALID_SIZE;
            SCFree(node);
            return NULL;
        }
        if (DecodeAsn1BuildValue(&d_ptr, &d_length, numbytes, errcode) == -1) {
            SCFree(node);
            return NULL;
        }
    }
    node->length = d_length + (d_ptr - buffer);
    if (node->length > max_size || node->length < d_length /* wrap */) {
        *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
        SCFree(node);
        return NULL;
    }

    parsed_bytes = 0;
    seq_index = 0;

    /* decode child elements */
    while (parsed_bytes < d_length) {
        el_max_size = max_size - (d_ptr-buffer);

        Asn1Generic *child = DecodeAsn1DerGeneric(d_ptr,
                MIN(node->length, el_max_size), depth,
                seq_index, errcode);
        if (child == NULL) {
            if (*errcode != 0) {
                DerFree(node);
                return NULL;
            }
            break;
        }

        int ret = Asn1SequenceAppend(node, child);
        if (ret == -1) {
            DerFree(child);
            break;
        }

        parsed_bytes += child->length;
        d_ptr += child->length;
        seq_index++;
    }

    return (Asn1Generic *)node;
}

static Asn1Generic * DecodeAsn1DerSet(const unsigned char *buffer,
                                      uint32_t max_size, uint8_t depth,
                                      uint32_t *errcode)
{
    const unsigned char *d_ptr = buffer;
    uint32_t d_length, numbytes, el_max_size;
    uint8_t c;
    uint32_t seq_index;
    Asn1Generic *node;
    Asn1Generic *child;

    d_ptr++;

    node = Asn1GenericNew();
    if (node == NULL) {
        *errcode = ERR_DER_GENERIC;
        return NULL;
    }
    node->type = ASN1_SET;
    node->data = NULL;

    /* total sequence length */
    c = d_ptr[0];
    /* short form 8.1.3.4 */
    if ((c & (1<<7))>>7 == 0) {
        d_length = c;
        d_ptr++;
    /* long form 8.1.3.5 */
    } else {
        numbytes = c & 0x7f;
        if (2 + numbytes > max_size) {
            *errcode = ERR_DER_INVALID_SIZE;
            SCFree(node);
            return NULL;
        }
        d_ptr++;
        if (DecodeAsn1BuildValue(&d_ptr, &d_length, numbytes, errcode) == -1) {
            SCFree(node);
            return NULL;
        }
    }

    node->length = d_length + (d_ptr - buffer);

    if (node->length > max_size || node->length < d_length /* wrap */) {
        *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
        SCFree(node);
        return NULL;
    }

    seq_index = 0;

    el_max_size = max_size - (d_ptr-buffer);

    child = DecodeAsn1DerGeneric(d_ptr, MIN(node->length, el_max_size),
            depth, seq_index, errcode);
    if (child == NULL) {
        DerFree(node);
        return NULL;
    }

    node->data = child;

    return (Asn1Generic *)node;
}

static Asn1Generic * DecodeAsn1DerT61String(const unsigned char *buffer,
                                            uint32_t max_size, uint8_t depth,
                                            uint32_t *errcode)
{
    Asn1Generic *a;

    a = DecodeAsn1DerIA5String(buffer, max_size, depth, errcode);
    if (a != NULL)
        a->type = ASN1_T61STRING;

    return a;
}

static Asn1Generic * DecodeAsn1DerUTCTime(const unsigned char *buffer,
                                          uint32_t max_size, uint8_t depth,
                                          uint32_t *errcode)
{
    Asn1Generic *a;

    a = DecodeAsn1DerIA5String(buffer, max_size, depth, errcode);
    if (a != NULL)
        a->type = ASN1_UTCTIME;

    return a;
}

static Asn1Generic * DecodeAsn1DerGeneralizedTime(const unsigned char *buffer,
                                                  uint32_t max_size,
                                                  uint8_t depth,
                                                  uint32_t *errcode)
{
    Asn1Generic *a;

    a = DecodeAsn1DerIA5String(buffer, max_size, depth, errcode);
    if (a != NULL)
        a->type = ASN1_GENERALIZEDTIME;

    return a;
}

/**
 *  \param errcode pointer to error code variable. May not be NULL.
 */
Asn1Generic * DecodeDer(const unsigned char *buffer, uint32_t size,
                        uint32_t *errcode)
{
    const unsigned char *d_ptr = buffer;
    uint32_t d_length, numbytes;
    Asn1Generic *cert;
    uint8_t c;

    DEBUG_VALIDATE_BUG_ON(errcode == NULL);
    *errcode = 0;

    if (size < 2) {
        *errcode = ERR_DER_INVALID_SIZE;
        return NULL;
    }

    /* check that buffer is an ASN.1 structure (basic checks) */
    if (d_ptr[0] != 0x30 && d_ptr[1] != 0x82) { /* Sequence */
        *errcode = ERR_DER_UNKNOWN_ELEMENT;
        return NULL;
    }

    c = d_ptr[1];
    if ((c & (1<<7))>>7 != 1) {
        *errcode = ERR_DER_INVALID_SIZE;
        return NULL;
    }

    numbytes = c & 0x7f;
    d_ptr += 2;
    if (size < numbytes + 2) {
        *errcode = ERR_DER_INVALID_SIZE;
        return NULL;
    }
    if (DecodeAsn1BuildValue(&d_ptr, &d_length, numbytes, errcode) == -1) {
        return NULL;
    }

    if (d_length+(d_ptr-buffer) != size) {
        *errcode = ERR_DER_INVALID_SIZE;
        return NULL;
    }

    cert = DecodeAsn1DerGeneric(buffer, size, 0 /* depth */, 0, errcode);

    return cert;
}

#ifdef AFLFUZZ_DER
int DerParseDataFromFile(char *filename)
{
    uint8_t buffer[65536];
    uint32_t errcode = 0;

#ifdef AFLFUZZ_PERSISTANT_MODE
    while (__AFL_LOOP(1000)) {
        /* reset state */
        memset(buffer, 0, sizeof(buffer));
#endif /* AFLFUZZ_PERSISTANT_MODE */

        FILE *fp = fopen(filename, "r");
        BUG_ON(fp == NULL);

        size_t result = fread(&buffer, 1, sizeof(buffer), fp);
        Asn1Generic *a = DecodeDer(buffer, result, &errcode);
        DerFree(a);
        fclose(fp);

#ifdef AFLFUZZ_PERSISTANT_MODE
    }
#endif /* AFLFUZZ_PERSISTANT_MODE */

    return 0;
}
#endif /* AFLFUZZ_DER */

void DerFree(Asn1Generic *a)
{
    Asn1Generic *it, *n;

    if (a == NULL)
        return;

    it = a;
    while (it) {
        n = it->next;
        if (it->data) {
            DerFree(it->data);
        }
        if (it->str)
            SCFree(it->str);
        memset(it, 0xff, sizeof(Asn1Generic));
        SCFree(it);
        it = n;
    }
}
