/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
 * All rights reserved.
 *
 * This package is an SSL implementation written
 * by Eric Young (eay@cryptsoft.com).
 * The implementation was written so as to conform with Netscapes SSL.
 *
 * This library is free for commercial and non-commercial use as long as
 * the following conditions are aheared to.  The following conditions
 * apply to all code found in this distribution, be it the RC4, RSA,
 * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
 * included with this distribution is covered by the same copyright terms
 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
 *
 * Copyright remains Eric Young's, and as such any Copyright notices in
 * the code are not to be removed.
 * If this package is used in a product, Eric Young should be given attribution
 * as the author of the parts of the library used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 *
 * 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 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    "This product includes cryptographic software written by
 *     Eric Young (eay@cryptsoft.com)"
 *    The word 'cryptographic' can be left out if the rouines from the library
 *    being used are not cryptographic related :-).
 * 4. If you include any Windows specific code (or a derivative thereof) from
 *    the apps directory (application code) you must include an acknowledgement:
 *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
 *
 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 OR CONTRIBUTORS 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.
 *
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.] */

#include <openssl_grpc/asn1.h>

#include <assert.h>
#include <limits.h>
#include <string.h>

#include <openssl_grpc/asn1t.h>
#include <openssl_grpc/mem.h>

#include "../internal.h"
#include "internal.h"


static int asn1_item_ex_i2d_opt(ASN1_VALUE **pval, unsigned char **out,
                                const ASN1_ITEM *it, int tag, int aclass,
                                int optional);
static int asn1_i2d_ex_primitive(ASN1_VALUE **pval, unsigned char **out,
                                 const ASN1_ITEM *it, int tag, int aclass,
                                 int optional);
static int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cont, int *out_omit,
                       int *putype, const ASN1_ITEM *it);
static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out,
                            int skcontlen, const ASN1_ITEM *item, int do_sort);
static int asn1_template_ex_i2d(ASN1_VALUE **pval, unsigned char **out,
                                const ASN1_TEMPLATE *tt, int tag, int aclass);

/*
 * Top level i2d equivalents
 */

int ASN1_item_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it)
{
    if (out && !*out) {
        unsigned char *p, *buf;
        int len = ASN1_item_ex_i2d(&val, NULL, it, /*tag=*/-1, /*aclass=*/0);
        if (len <= 0) {
            return len;
        }
        buf = OPENSSL_malloc(len);
        if (!buf) {
            OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
            return -1;
        }
        p = buf;
        int len2 = ASN1_item_ex_i2d(&val, &p, it, /*tag=*/-1, /*aclass=*/0);
        if (len2 <= 0) {
            return len2;
        }
        assert(len == len2);
        *out = buf;
        return len;
    }

    return ASN1_item_ex_i2d(&val, out, it, /*tag=*/-1, /*aclass=*/0);
}

/*
 * Encode an item, taking care of IMPLICIT tagging (if any). This function
 * performs the normal item handling: it can be used in external types.
 */

int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out,
                     const ASN1_ITEM *it, int tag, int aclass)
{
    int ret = asn1_item_ex_i2d_opt(pval, out, it, tag, aclass, /*optional=*/0);
    assert(ret != 0);
    return ret;
}

/* asn1_item_ex_i2d_opt behaves like |ASN1_item_ex_i2d| but, if |optional| is
 * non-zero and |*pval| is omitted, it returns zero and writes no bytes. */
int asn1_item_ex_i2d_opt(ASN1_VALUE **pval, unsigned char **out,
                         const ASN1_ITEM *it, int tag, int aclass,
                         int optional)
{
    const ASN1_TEMPLATE *tt = NULL;
    int i, seqcontlen, seqlen;

    /* Historically, |aclass| was repurposed to pass additional flags into the
     * encoding process. */
    assert((aclass & ASN1_TFLG_TAG_CLASS) == aclass);
    /* If not overridding the tag, |aclass| is ignored and should be zero. */
    assert(tag != -1 || aclass == 0);

    /* All fields are pointers, except for boolean |ASN1_ITYPE_PRIMITIVE|s.
     * Optional primitives are handled later. */
    if ((it->itype != ASN1_ITYPE_PRIMITIVE) && !*pval) {
        if (optional) {
            return 0;
        }
        OPENSSL_PUT_ERROR(ASN1, ASN1_R_MISSING_VALUE);
        return -1;
    }

    switch (it->itype) {

    case ASN1_ITYPE_PRIMITIVE:
        if (it->templates) {
            if (it->templates->flags & ASN1_TFLG_OPTIONAL) {
                OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
                return -1;
            }
            return asn1_template_ex_i2d(pval, out, it->templates, tag, aclass);
        }
        return asn1_i2d_ex_primitive(pval, out, it, tag, aclass, optional);

    case ASN1_ITYPE_MSTRING:
        /*
         * It never makes sense for multi-strings to have implicit tagging, so
         * if tag != -1, then this looks like an error in the template.
         */
        if (tag != -1) {
            OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
            return -1;
        }
        return asn1_i2d_ex_primitive(pval, out, it, -1, 0, optional);

    case ASN1_ITYPE_CHOICE: {
        /*
         * It never makes sense for CHOICE types to have implicit tagging, so if
         * tag != -1, then this looks like an error in the template.
         */
        if (tag != -1) {
            OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
            return -1;
        }
        i = asn1_get_choice_selector(pval, it);
        if (i < 0 || i >= it->tcount) {
            OPENSSL_PUT_ERROR(ASN1, ASN1_R_NO_MATCHING_CHOICE_TYPE);
            return -1;
        }
        const ASN1_TEMPLATE *chtt = it->templates + i;
        if (chtt->flags & ASN1_TFLG_OPTIONAL) {
            OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
            return -1;
        }
        ASN1_VALUE **pchval = asn1_get_field_ptr(pval, chtt);
        return asn1_template_ex_i2d(pchval, out, chtt, -1, 0);
    }

    case ASN1_ITYPE_EXTERN: {
        /* If new style i2d it does all the work */
        const ASN1_EXTERN_FUNCS *ef = it->funcs;
        int ret = ef->asn1_ex_i2d(pval, out, it, tag, aclass);
        if (ret == 0) {
            /* |asn1_ex_i2d| should never return zero. We have already checked
             * for optional values generically, and |ASN1_ITYPE_EXTERN| fields
             * must be pointers. */
            OPENSSL_PUT_ERROR(ASN1, ERR_R_INTERNAL_ERROR);
            return -1;
        }
        return ret;
    }

    case ASN1_ITYPE_SEQUENCE: {
        i = asn1_enc_restore(&seqcontlen, out, pval, it);
        /* An error occurred */
        if (i < 0)
            return -1;
        /* We have a valid cached encoding... */
        if (i > 0)
            return seqcontlen;
        /* Otherwise carry on */
        seqcontlen = 0;
        /* If no IMPLICIT tagging set to SEQUENCE, UNIVERSAL */
        if (tag == -1) {
            tag = V_ASN1_SEQUENCE;
            aclass = V_ASN1_UNIVERSAL;
        }
        /* First work out sequence content length */
        for (i = 0, tt = it->templates; i < it->tcount; tt++, i++) {
            const ASN1_TEMPLATE *seqtt;
            ASN1_VALUE **pseqval;
            int tmplen;
            seqtt = asn1_do_adb(pval, tt, 1);
            if (!seqtt)
                return -1;
            pseqval = asn1_get_field_ptr(pval, seqtt);
            tmplen = asn1_template_ex_i2d(pseqval, NULL, seqtt, -1, 0);
            if (tmplen == -1 || (tmplen > INT_MAX - seqcontlen))
                return -1;
            seqcontlen += tmplen;
        }

        seqlen = ASN1_object_size(/*constructed=*/1, seqcontlen, tag);
        if (!out || seqlen == -1)
            return seqlen;
        /* Output SEQUENCE header */
        ASN1_put_object(out, /*constructed=*/1, seqcontlen, tag, aclass);
        for (i = 0, tt = it->templates; i < it->tcount; tt++, i++) {
            const ASN1_TEMPLATE *seqtt;
            ASN1_VALUE **pseqval;
            seqtt = asn1_do_adb(pval, tt, 1);
            if (!seqtt)
                return -1;
            pseqval = asn1_get_field_ptr(pval, seqtt);
            if (asn1_template_ex_i2d(pseqval, out, seqtt, -1, 0) < 0) {
                return -1;
            }
        }
        return seqlen;
    }

    default:
        OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
        return -1;
    }
}

/* asn1_template_ex_i2d behaves like |asn1_item_ex_i2d_opt| but uses an
 * |ASN1_TEMPLATE| instead of an |ASN1_ITEM|. An |ASN1_TEMPLATE| wraps an
 * |ASN1_ITEM| with modifiers such as tagging, SEQUENCE or SET, etc. Instead of
 * taking an |optional| parameter, it uses the |ASN1_TFLG_OPTIONAL| flag. */
static int asn1_template_ex_i2d(ASN1_VALUE **pval, unsigned char **out,
                                const ASN1_TEMPLATE *tt, int tag, int iclass)
{
    int i, ret, flags, ttag, tclass;
    size_t j;
    flags = tt->flags;

    /* Historically, |iclass| was repurposed to pass additional flags into the
     * encoding process. */
    assert((iclass & ASN1_TFLG_TAG_CLASS) == iclass);
    /* If not overridding the tag, |iclass| is ignored and should be zero. */
    assert(tag != -1 || iclass == 0);

    /*
     * Work out tag and class to use: tagging may come either from the
     * template or the arguments, not both because this would create
     * ambiguity.
     */
    if (flags & ASN1_TFLG_TAG_MASK) {
        /* Error if argument and template tagging */
        if (tag != -1) {
            OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
            return -1;
        }
        /* Get tagging from template */
        ttag = tt->tag;
        tclass = flags & ASN1_TFLG_TAG_CLASS;
    } else if (tag != -1) {
        /* No template tagging, get from arguments */
        ttag = tag;
        tclass = iclass & ASN1_TFLG_TAG_CLASS;
    } else {
        ttag = -1;
        tclass = 0;
    }

    const int optional = (flags & ASN1_TFLG_OPTIONAL) != 0;

    /*
     * At this point 'ttag' contains the outer tag to use, and 'tclass' is the
     * class.
     */

    if (flags & ASN1_TFLG_SK_MASK) {
        /* SET OF, SEQUENCE OF */
        STACK_OF(ASN1_VALUE) *sk = (STACK_OF(ASN1_VALUE) *)*pval;
        int isset, sktag, skaclass;
        int skcontlen, sklen;
        ASN1_VALUE *skitem;

        if (!*pval) {
            if (optional) {
                return 0;
            }
            OPENSSL_PUT_ERROR(ASN1, ASN1_R_MISSING_VALUE);
            return -1;
        }

        if (flags & ASN1_TFLG_SET_OF) {
            isset = 1;
            /* Historically, types with both bits set were mutated when
             * serialized to apply the sort. We no longer support this. */
            assert((flags & ASN1_TFLG_SEQUENCE_OF) == 0);
        } else {
            isset = 0;
        }

        /*
         * Work out inner tag value: if EXPLICIT or no tagging use underlying
         * type.
         */
        if ((ttag != -1) && !(flags & ASN1_TFLG_EXPTAG)) {
            sktag = ttag;
            skaclass = tclass;
        } else {
            skaclass = V_ASN1_UNIVERSAL;
            if (isset)
                sktag = V_ASN1_SET;
            else
                sktag = V_ASN1_SEQUENCE;
        }

        /* Determine total length of items */
        skcontlen = 0;
        for (j = 0; j < sk_ASN1_VALUE_num(sk); j++) {
            int tmplen;
            skitem = sk_ASN1_VALUE_value(sk, j);
            tmplen = ASN1_item_ex_i2d(&skitem, NULL, ASN1_ITEM_ptr(tt->item),
                                      -1, 0);
            if (tmplen == -1 || (skcontlen > INT_MAX - tmplen))
                return -1;
            skcontlen += tmplen;
        }
        sklen = ASN1_object_size(/*constructed=*/1, skcontlen, sktag);
        if (sklen == -1)
            return -1;
        /* If EXPLICIT need length of surrounding tag */
        if (flags & ASN1_TFLG_EXPTAG)
            ret = ASN1_object_size(/*constructed=*/1, sklen, ttag);
        else
            ret = sklen;

        if (!out || ret == -1)
            return ret;

        /* Now encode this lot... */
        /* EXPLICIT tag */
        if (flags & ASN1_TFLG_EXPTAG)
            ASN1_put_object(out, /*constructed=*/1, sklen, ttag, tclass);
        /* SET or SEQUENCE and IMPLICIT tag */
        ASN1_put_object(out, /*constructed=*/1, skcontlen, sktag, skaclass);
        /* And the stuff itself */
        if (!asn1_set_seq_out(sk, out, skcontlen, ASN1_ITEM_ptr(tt->item),
                              isset)) {
            return -1;
        }
        return ret;
    }

    if (flags & ASN1_TFLG_EXPTAG) {
        /* EXPLICIT tagging */
        /* Find length of tagged item */
        i = asn1_item_ex_i2d_opt(pval, NULL, ASN1_ITEM_ptr(tt->item), -1, 0,
                                 optional);
        if (i <= 0)
            return i;
        /* Find length of EXPLICIT tag */
        ret = ASN1_object_size(/*constructed=*/1, i, ttag);
        if (out && ret != -1) {
            /* Output tag and item */
            ASN1_put_object(out, /*constructed=*/1, i, ttag, tclass);
            if (ASN1_item_ex_i2d(pval, out, ASN1_ITEM_ptr(tt->item), -1,
                                 0) < 0) {
                return -1;
            }
        }
        return ret;
    }

    /* Either normal or IMPLICIT tagging */
    return asn1_item_ex_i2d_opt(pval, out, ASN1_ITEM_ptr(tt->item),
                                ttag, tclass, optional);

}

/* Temporary structure used to hold DER encoding of items for SET OF */

typedef struct {
    unsigned char *data;
    int length;
} DER_ENC;

static int der_cmp(const void *a, const void *b)
{
    const DER_ENC *d1 = a, *d2 = b;
    int cmplen, i;
    cmplen = (d1->length < d2->length) ? d1->length : d2->length;
    i = OPENSSL_memcmp(d1->data, d2->data, cmplen);
    if (i)
        return i;
    return d1->length - d2->length;
}

/* asn1_set_seq_out writes |sk| to |out| under the i2d output convention,
 * excluding the tag and length. It returns one on success and zero on error.
 * |skcontlen| must be the total encoded size. If |do_sort| is non-zero, the
 * elements are sorted for a SET OF type. Each element of |sk| has type
 * |item|. */
static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out,
                            int skcontlen, const ASN1_ITEM *item, int do_sort)
{
    /* No need to sort if there are fewer than two items. */
    if (!do_sort || sk_ASN1_VALUE_num(sk) < 2) {
        for (size_t i = 0; i < sk_ASN1_VALUE_num(sk); i++) {
            ASN1_VALUE *skitem = sk_ASN1_VALUE_value(sk, i);
            if (ASN1_item_ex_i2d(&skitem, out, item, -1, 0) < 0) {
                return 0;
            }
        }
        return 1;
    }

    if (sk_ASN1_VALUE_num(sk) > ((size_t)-1) / sizeof(DER_ENC)) {
        OPENSSL_PUT_ERROR(ASN1, ERR_R_OVERFLOW);
        return 0;
    }

    int ret = 0;
    unsigned char *const buf = OPENSSL_malloc(skcontlen);
    DER_ENC *encoded = OPENSSL_malloc(sk_ASN1_VALUE_num(sk) * sizeof(*encoded));
    if (encoded == NULL || buf == NULL) {
        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
        goto err;
    }

    /* Encode all the elements into |buf| and populate |encoded|. */
    unsigned char *p = buf;
    for (size_t i = 0; i < sk_ASN1_VALUE_num(sk); i++) {
        ASN1_VALUE *skitem = sk_ASN1_VALUE_value(sk, i);
        encoded[i].data = p;
        encoded[i].length = ASN1_item_ex_i2d(&skitem, &p, item, -1, 0);
        if (encoded[i].length < 0) {
            goto err;
        }
        assert(p - buf <= skcontlen);
    }

    qsort(encoded, sk_ASN1_VALUE_num(sk), sizeof(*encoded), der_cmp);

    /* Output the elements in sorted order. */
    p = *out;
    for (size_t i = 0; i < sk_ASN1_VALUE_num(sk); i++) {
        OPENSSL_memcpy(p, encoded[i].data, encoded[i].length);
        p += encoded[i].length;
    }
    *out = p;

    ret = 1;

err:
    OPENSSL_free(encoded);
    OPENSSL_free(buf);
    return ret;
}

/* asn1_i2d_ex_primitive behaves like |ASN1_item_ex_i2d| but |item| must be a
 * a PRIMITIVE or MSTRING type that is not an |ASN1_ITEM_TEMPLATE|. */
static int asn1_i2d_ex_primitive(ASN1_VALUE **pval, unsigned char **out,
                                 const ASN1_ITEM *it, int tag, int aclass,
                                 int optional)
{
    /* Get length of content octets and maybe find out the underlying type. */
    int omit;
    int utype = it->utype;
    int len = asn1_ex_i2c(pval, NULL, &omit, &utype, it);
    if (len < 0) {
        return -1;
    }
    if (omit) {
        if (optional) {
            return 0;
        }
        OPENSSL_PUT_ERROR(ASN1, ASN1_R_MISSING_VALUE);
        return -1;
    }

    /*
     * If SEQUENCE, SET or OTHER then header is included in pseudo content
     * octets so don't include tag+length. We need to check here because the
     * call to asn1_ex_i2c() could change utype.
     */
    int usetag = utype != V_ASN1_SEQUENCE && utype != V_ASN1_SET &&
                 utype != V_ASN1_OTHER;

    /* If not implicitly tagged get tag from underlying type */
    if (tag == -1)
        tag = utype;

    /* Output tag+length followed by content octets */
    if (out) {
        if (usetag) {
            ASN1_put_object(out, /*constructed=*/0, len, tag, aclass);
        }
        int len2 = asn1_ex_i2c(pval, *out, &omit, &utype, it);
        if (len2 < 0) {
          return -1;
        }
        assert(len == len2);
        assert(!omit);
        *out += len;
    }

    if (usetag) {
        return ASN1_object_size(/*constructed=*/0, len, tag);
    }
    return len;
}

/* asn1_ex_i2c writes the |*pval| to |cout| under the i2d output convention,
 * excluding the tag and length. It returns the number of bytes written,
 * possibly zero, on success or -1 on error. If |*pval| should be omitted, it
 * returns zero and sets |*out_omit| to true.
 *
 * If |it| is an MSTRING or ANY type, it gets the underlying type from |*pval|,
 * which must be an |ASN1_STRING| or |ASN1_TYPE|, respectively. It then updates
 * |*putype| with the tag number of type used, or |V_ASN1_OTHER| if it was not a
 * universal type. If |*putype| is set to |V_ASN1_SEQUENCE|, |V_ASN1_SET|, or
 * |V_ASN1_OTHER|, it additionally outputs the tag and length, so the caller
 * must not do so.
 *
 * Otherwise, |*putype| must contain |it->utype|.
 *
 * WARNING: Unlike most functions in this file, |asn1_ex_i2c| can return zero
 * without omitting the element. ASN.1 values may have empty contents. */
static int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cout, int *out_omit,
                       int *putype, const ASN1_ITEM *it)
{
    ASN1_BOOLEAN *tbool = NULL;
    ASN1_STRING *strtmp;
    ASN1_OBJECT *otmp;
    int utype;
    const unsigned char *cont;
    unsigned char c;
    int len;

    /* Historically, |it->funcs| for primitive types contained an
     * |ASN1_PRIMITIVE_FUNCS| table of callbacks. */
    assert(it->funcs == NULL);

    *out_omit = 0;

    /* Should type be omitted? */
    if ((it->itype != ASN1_ITYPE_PRIMITIVE)
        || (it->utype != V_ASN1_BOOLEAN)) {
        if (!*pval) {
            *out_omit = 1;
            return 0;
        }
    }

    if (it->itype == ASN1_ITYPE_MSTRING) {
        /* If MSTRING type set the underlying type */
        strtmp = (ASN1_STRING *)*pval;
        utype = strtmp->type;
        if (utype < 0 && utype != V_ASN1_OTHER) {
            /* MSTRINGs can have type -1 when default-constructed. */
            OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE);
            return -1;
        }
        /* Negative INTEGER and ENUMERATED values use |ASN1_STRING| type values
         * that do not match their corresponding utype values. INTEGERs cannot
         * participate in MSTRING types, but ENUMERATEDs can.
         *
         * TODO(davidben): Is this a bug? Although arguably one of the MSTRING
         * types should contain more values, rather than less. See
         * https://crbug.com/boringssl/412. But it is not possible to fit all
         * possible ANY values into an |ASN1_STRING|, so matching the spec here
         * is somewhat hopeless. */
        if (utype == V_ASN1_NEG_INTEGER) {
            utype = V_ASN1_INTEGER;
        } else if (utype == V_ASN1_NEG_ENUMERATED) {
            utype = V_ASN1_ENUMERATED;
        }
        *putype = utype;
    } else if (it->utype == V_ASN1_ANY) {
        /* If ANY set type and pointer to value */
        ASN1_TYPE *typ;
        typ = (ASN1_TYPE *)*pval;
        utype = typ->type;
        if (utype < 0 && utype != V_ASN1_OTHER) {
            /* |ASN1_TYPE|s can have type -1 when default-constructed. */
            OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE);
            return -1;
        }
        *putype = utype;
        pval = &typ->value.asn1_value;
    } else
        utype = *putype;

    switch (utype) {
    case V_ASN1_OBJECT:
        otmp = (ASN1_OBJECT *)*pval;
        cont = otmp->data;
        len = otmp->length;
        if (len == 0) {
            /* Some |ASN1_OBJECT|s do not have OIDs and cannot be serialized. */
            OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_OBJECT);
            return -1;
        }
        break;

    case V_ASN1_NULL:
        cont = NULL;
        len = 0;
        break;

    case V_ASN1_BOOLEAN:
        tbool = (ASN1_BOOLEAN *)pval;
        if (*tbool == -1) {
            *out_omit = 1;
            return 0;
        }
        if (it->utype != V_ASN1_ANY) {
            /*
             * Default handling if value == size field then omit
             */
            if ((*tbool && (it->size > 0)) ||
                (!*tbool && !it->size)) {
                *out_omit = 1;
                return 0;
            }
        }
        c = *tbool ? 0xff : 0x00;
        cont = &c;
        len = 1;
        break;

    case V_ASN1_BIT_STRING: {
        int ret = i2c_ASN1_BIT_STRING((ASN1_BIT_STRING *)*pval,
                                      cout ? &cout : NULL);
        /* |i2c_ASN1_BIT_STRING| returns zero on error instead of -1. */
        return ret <= 0 ? -1 : ret;
    }

    case V_ASN1_INTEGER:
    case V_ASN1_ENUMERATED: {
        /* |i2c_ASN1_INTEGER| also handles ENUMERATED. */
        int ret = i2c_ASN1_INTEGER((ASN1_INTEGER *)*pval, cout ? &cout : NULL);
        /* |i2c_ASN1_INTEGER| returns zero on error instead of -1. */
        return ret <= 0 ? -1 : ret;
    }

    case V_ASN1_OCTET_STRING:
    case V_ASN1_NUMERICSTRING:
    case V_ASN1_PRINTABLESTRING:
    case V_ASN1_T61STRING:
    case V_ASN1_VIDEOTEXSTRING:
    case V_ASN1_IA5STRING:
    case V_ASN1_UTCTIME:
    case V_ASN1_GENERALIZEDTIME:
    case V_ASN1_GRAPHICSTRING:
    case V_ASN1_VISIBLESTRING:
    case V_ASN1_GENERALSTRING:
    case V_ASN1_UNIVERSALSTRING:
    case V_ASN1_BMPSTRING:
    case V_ASN1_UTF8STRING:
    case V_ASN1_SEQUENCE:
    case V_ASN1_SET:
    default:
        /* All based on ASN1_STRING and handled the same */
        strtmp = (ASN1_STRING *)*pval;
        cont = strtmp->data;
        len = strtmp->length;

        break;

    }
    if (cout && len)
        OPENSSL_memcpy(cout, cont, len);
    return len;
}
