/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "secder.h"
#include "secerr.h"

#if 0
/*
 * Generic templates for individual/simple items.
 */

DERTemplate SECAnyTemplate[] = {
    { DER_ANY,
	  0, NULL, sizeof(SECItem) }
};

DERTemplate SECBitStringTemplate[] = {
    { DER_BIT_STRING,
	  0, NULL, sizeof(SECItem) }
};

DERTemplate SECBooleanTemplate[] = {
    { DER_BOOLEAN,
	  0, NULL, sizeof(SECItem) }
};

DERTemplate SECIA5StringTemplate[] = {
    { DER_IA5_STRING,
	  0, NULL, sizeof(SECItem) }
};

DERTemplate SECIntegerTemplate[] = {
    { DER_INTEGER,
	  0, NULL, sizeof(SECItem) }
};

DERTemplate SECNullTemplate[] = {
    { DER_NULL,
	  0, NULL, sizeof(SECItem) }
};

DERTemplate SECObjectIDTemplate[] = {
    { DER_OBJECT_ID,
	  0, NULL, sizeof(SECItem) }
};

DERTemplate SECOctetStringTemplate[] = {
    { DER_OCTET_STRING,
	  0, NULL, sizeof(SECItem) }
};

DERTemplate SECPrintableStringTemplate[] = {
    { DER_PRINTABLE_STRING,
	  0, NULL, sizeof(SECItem) }
};

DERTemplate SECT61StringTemplate[] = {
    { DER_T61_STRING,
	  0, NULL, sizeof(SECItem) }
};

DERTemplate SECUTCTimeTemplate[] = {
    { DER_UTC_TIME,
	  0, NULL, sizeof(SECItem) }
};

#endif

static int
header_length(DERTemplate *dtemplate, PRUint32 contents_len)
{
    PRUint32 len;
    unsigned long encode_kind, under_kind;
    PRBool explicit, optional, universal;

    encode_kind = dtemplate->kind;

    explicit = (encode_kind & DER_EXPLICIT) ? PR_TRUE : PR_FALSE;
    optional = (encode_kind & DER_OPTIONAL) ? PR_TRUE : PR_FALSE;
    universal = ((encode_kind & DER_CLASS_MASK) == DER_UNIVERSAL)
		? PR_TRUE : PR_FALSE;

    PORT_Assert (!(explicit && universal));	/* bad templates */

    if (encode_kind & DER_POINTER) {
	if (dtemplate->sub != NULL) {
	    under_kind = dtemplate->sub->kind;
	    if (universal) {
		encode_kind = under_kind;
	    }
	} else if (universal) {
	    under_kind = encode_kind & ~DER_POINTER;
	} else {
	    under_kind = dtemplate->arg;
	}
    } else if (encode_kind & DER_INLINE) {
	PORT_Assert (dtemplate->sub != NULL);
	under_kind = dtemplate->sub->kind;
	if (universal) {
	    encode_kind = under_kind;
	}
    } else if (universal) {
	under_kind = encode_kind;
    } else {
	under_kind = dtemplate->arg;
    }

    /* This is only used in decoding; it plays no part in encoding.  */
    if (under_kind & DER_DERPTR)
	return 0;

    /* No header at all for an "empty" optional.  */
    if ((contents_len == 0) && optional)
	return 0;

    /* And no header for a full DER_ANY.  */
    if (encode_kind & DER_ANY)
	return 0;

    /*
     * The common case: one octet for identifier and as many octets
     * as necessary to hold the content length.
     */
    len = 1 + DER_LengthLength(contents_len);

    /* Account for the explicit wrapper, if necessary.  */
    if (explicit) {
#if 0		/*
		 * Well, I was trying to do something useful, but these
		 * assertions are too restrictive on valid templates.
		 * I wanted to make sure that the top-level "kind" of
		 * a template does not also specify DER_EXPLICIT, which
		 * should only modify a component field.  Maybe later
		 * I can figure out a better way to detect such a problem,
		 * but for now I must remove these checks altogether.
		 */
	/*
	 * This modifier applies only to components of a set or sequence;
	 * it should never be used on a set/sequence itself -- confirm.
	 */
	PORT_Assert (under_kind != DER_SEQUENCE);
	PORT_Assert (under_kind != DER_SET);
#endif

	len += 1 + DER_LengthLength(len + contents_len);
    }

    return len;
}


static PRUint32
contents_length(DERTemplate *dtemplate, void *src)
{
    PRUint32 len;
    unsigned long encode_kind, under_kind;
    PRBool universal;


    PORT_Assert (src != NULL);

    encode_kind = dtemplate->kind;

    universal = ((encode_kind & DER_CLASS_MASK) == DER_UNIVERSAL)
		? PR_TRUE : PR_FALSE;
    encode_kind &= ~DER_OPTIONAL;

    if (encode_kind & DER_POINTER) {
	src = *(void **)src;
	if (src == NULL) {
	    return 0;
	}
	if (dtemplate->sub != NULL) {
	    dtemplate = dtemplate->sub;
	    under_kind = dtemplate->kind;
	    src = (void *)((char *)src + dtemplate->offset);
	} else if (universal) {
	    under_kind = encode_kind & ~DER_POINTER;
	} else {
	    under_kind = dtemplate->arg;
	}
    } else if (encode_kind & DER_INLINE) {
	PORT_Assert (dtemplate->sub != NULL);
	dtemplate = dtemplate->sub;
	under_kind = dtemplate->kind;
	src = (void *)((char *)src + dtemplate->offset);
    } else if (universal) {
	under_kind = encode_kind;
    } else {
	under_kind = dtemplate->arg;
    }

    /* Having any of these bits is not expected here...  */
    PORT_Assert ((under_kind & (DER_EXPLICIT | DER_INLINE | DER_OPTIONAL
				| DER_POINTER | DER_SKIP)) == 0);

    /* This is only used in decoding; it plays no part in encoding.  */
    if (under_kind & DER_DERPTR)
	return 0;

    if (under_kind & DER_INDEFINITE) {
	PRUint32 sub_len;
	void   **indp = *(void ***)src;

	if (indp == NULL)
	    return 0;

	len = 0;
	under_kind &= ~DER_INDEFINITE;

	if (under_kind == DER_SET || under_kind == DER_SEQUENCE) {
	    DERTemplate *tmpt = dtemplate->sub;
	    PORT_Assert (tmpt != NULL);

	    for (; *indp != NULL; indp++) {
		void *sub_src = (void *)((char *)(*indp) + tmpt->offset);
		sub_len = contents_length (tmpt, sub_src);
		len += sub_len + header_length (tmpt, sub_len);
	    }
	} else {
	    /*
	     * XXX Lisa is not sure this code (for handling, for example,
	     * DER_INDEFINITE | DER_OCTET_STRING) is right.
	     */
	    for (; *indp != NULL; indp++) {
		SECItem *item = (SECItem *)(*indp);
		sub_len = item->len;
		if (under_kind == DER_BIT_STRING) {
		    sub_len = (sub_len + 7) >> 3;
		    /* bit string contents involve an extra octet */
		    if (sub_len)
			sub_len++;
		}
		if (under_kind != DER_ANY)
		    len += 1 + DER_LengthLength (sub_len);
	    }
	}

	return len;
    }

    switch (under_kind) {
      case DER_SEQUENCE:
      case DER_SET:
	{
	    DERTemplate *tmpt;
	    void *sub_src;
	    PRUint32 sub_len;

	    len = 0;
	    for (tmpt = dtemplate + 1; tmpt->kind; tmpt++) {
		sub_src = (void *)((char *)src + tmpt->offset);
		sub_len = contents_length (tmpt, sub_src);
		len += sub_len + header_length (tmpt, sub_len);
	    }
	}
	break;

      case DER_BIT_STRING:
	len = (((SECItem *)src)->len + 7) >> 3;
	/* bit string contents involve an extra octet */
	if (len)
	    len++;
	break;

      default:
	len = ((SECItem *)src)->len;
	break;
    }

    return len;
}


static unsigned char *
der_encode(unsigned char *buf, DERTemplate *dtemplate, void *src)
{
    int header_len;
    PRUint32 contents_len;
    unsigned long encode_kind, under_kind;
    PRBool explicit, universal;


    /*
     * First figure out how long the encoding will be.  Do this by
     * traversing the template from top to bottom and accumulating
     * the length of each leaf item.
     */
    contents_len = contents_length (dtemplate, src);
    header_len = header_length (dtemplate, contents_len);

    /*
     * Enough smarts was involved already, so that if both the
     * header and the contents have a length of zero, then we
     * are not doing any encoding for this element.
     */
    if (header_len == 0 && contents_len == 0)
	return buf;

    encode_kind = dtemplate->kind;

    explicit = (encode_kind & DER_EXPLICIT) ? PR_TRUE : PR_FALSE;
    encode_kind &= ~DER_OPTIONAL;
    universal = ((encode_kind & DER_CLASS_MASK) == DER_UNIVERSAL)
		? PR_TRUE : PR_FALSE;

    if (encode_kind & DER_POINTER) {
	if (contents_len) {
	    src = *(void **)src;
	    PORT_Assert (src != NULL);
	}
	if (dtemplate->sub != NULL) {
	    dtemplate = dtemplate->sub;
	    under_kind = dtemplate->kind;
	    if (universal) {
		encode_kind = under_kind;
	    }
	    src = (void *)((char *)src + dtemplate->offset);
	} else if (universal) {
	    under_kind = encode_kind & ~DER_POINTER;
	} else {
	    under_kind = dtemplate->arg;
	}
    } else if (encode_kind & DER_INLINE) {
	dtemplate = dtemplate->sub;
	under_kind = dtemplate->kind;
	if (universal) {
	    encode_kind = under_kind;
	}
	src = (void *)((char *)src + dtemplate->offset);
    } else if (universal) {
	under_kind = encode_kind;
    } else {
	under_kind = dtemplate->arg;
    }

    if (explicit) {
	buf = DER_StoreHeader (buf, encode_kind,
			       (1 + DER_LengthLength(contents_len)
				+ contents_len));
	encode_kind = under_kind;
    }

    if ((encode_kind & DER_ANY) == 0) {	/* DER_ANY already contains header */
	buf = DER_StoreHeader (buf, encode_kind, contents_len);
    }

    /* If no real contents to encode, then we are done.  */
    if (contents_len == 0)
	return buf;

    if (under_kind & DER_INDEFINITE) {
	void **indp;

	indp = *(void ***)src;
	PORT_Assert (indp != NULL);

	under_kind &= ~DER_INDEFINITE;
	if (under_kind == DER_SET || under_kind == DER_SEQUENCE) {
	    DERTemplate *tmpt = dtemplate->sub;
	    PORT_Assert (tmpt != NULL);
	    for (; *indp != NULL; indp++) {
		void *sub_src = (void *)((char *)(*indp) + tmpt->offset);
		buf = der_encode (buf, tmpt, sub_src);
	    }
	} else {
	    for (; *indp != NULL; indp++) {
		SECItem *item;
		int sub_len;

		item = (SECItem *)(*indp);
		sub_len = item->len;
		if (under_kind == DER_BIT_STRING) {
		    if (sub_len) {
			int rem;

			sub_len = (sub_len + 7) >> 3;
			buf = DER_StoreHeader (buf, under_kind, sub_len + 1);
			rem = (sub_len << 3) - item->len;
			*buf++ = rem;		/* remaining bits */
		    } else {
			buf = DER_StoreHeader (buf, under_kind, 0);
		    }
		} else if (under_kind != DER_ANY) {
		    buf = DER_StoreHeader (buf, under_kind, sub_len);
		}
		PORT_Memcpy (buf, item->data, sub_len);
		buf += sub_len;
	    }
	}
	return buf;
    }

    switch (under_kind) {
      case DER_SEQUENCE:
      case DER_SET:
	{
	    DERTemplate *tmpt;
	    void *sub_src;

	    for (tmpt = dtemplate + 1; tmpt->kind; tmpt++) {
		sub_src = (void *)((char *)src + tmpt->offset);
		buf = der_encode (buf, tmpt, sub_src);
	    }
	}
	break;

      case DER_BIT_STRING:
	{
	    SECItem *item;
	    int rem;

	    /*
	     * The contents length includes our extra octet; subtract
	     * it off so we just have the real string length there.
	     */
	    contents_len--;
	    item = (SECItem *)src;
	    PORT_Assert (contents_len == ((item->len + 7) >> 3));
	    rem = (contents_len << 3) - item->len;
	    *buf++ = rem;		/* remaining bits */
	    PORT_Memcpy (buf, item->data, contents_len);
	    buf += contents_len;
	}
	break;

      default:
	{
	    SECItem *item;

	    item = (SECItem *)src;
	    PORT_Assert (contents_len == item->len);
	    PORT_Memcpy (buf, item->data, contents_len);
	    buf += contents_len;
	}
	break;
    }

    return buf;
}


SECStatus
DER_Encode(PLArenaPool *arena, SECItem *dest, DERTemplate *dtemplate, void *src)
{
    unsigned int contents_len, header_len;

    src = (void **)((char *)src + dtemplate->offset);

    /*
     * First figure out how long the encoding will be. Do this by
     * traversing the template from top to bottom and accumulating
     * the length of each leaf item.
     */
    contents_len = contents_length (dtemplate, src);
    header_len = header_length (dtemplate, contents_len);

    dest->len = contents_len + header_len;

    /* Allocate storage to hold the encoding */
    dest->data = (unsigned char*) PORT_ArenaAlloc(arena, dest->len);
    if (dest->data == NULL) {
	PORT_SetError(SEC_ERROR_NO_MEMORY);
	return SECFailure;
    }

    /* Now encode into the buffer */
    (void) der_encode (dest->data, dtemplate, src);

    return SECSuccess;
}
