tasn_dec.c revision 9dc0df1bac950d6e491f9a7c7e4888f2b301cb15
/* tasn_dec.c */
/* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL
* project 2000.
*/
/* ====================================================================
* Copyright (c) 2000-2005 The OpenSSL Project. 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
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
*
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* licensing@OpenSSL.org.
*
* 5. Products derived from this software may not be called "OpenSSL"
* nor may "OpenSSL" appear in their names without prior written
* permission of the OpenSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
*
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED 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 OpenSSL PROJECT OR
* ITS 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.
* ====================================================================
*
* This product includes cryptographic software written by Eric Young
* (eay@cryptsoft.com). This product includes software written by Tim
* Hudson (tjh@cryptsoft.com).
*
*/
#include <stddef.h>
#include <string.h>
/* Table to convert tags to bit values, used for MSTRING type */
static unsigned long tag2bit[32] = {
0, 0, 0, B_ASN1_BIT_STRING, /* tags 0 - 3 */
};
unsigned long ASN1_tag2bit(int tag)
{
}
/* Macro to initialize and invalidate the cache */
#define asn1_tlc_clear(c) if (c) (c)->valid = 0
/* Decode an ASN1 item, this currently behaves just
* like a standard 'd2i' function. 'in' points to
* a buffer to read the data from, in future we will
* have more advanced versions that can input data
* a piece at a time and this will simply be a special
* case.
*/
{
ASN1_TLC c;
if (!pval)
asn1_tlc_clear(&c);
return *pval;
return NULL;
}
{
ASN1_TLC c;
asn1_tlc_clear(&c);
}
/* Decode an item, taking care of IMPLICIT tagging, if any.
* If 'opt' set and tag mismatch return -1 to handle OPTIONAL
*/
{
const ASN1_COMPAT_FUNCS *cf;
const ASN1_EXTERN_FUNCS *ef;
const unsigned char *p, *q;
long tmplen;
int i;
int otag;
int ret = 0;
if (!pval)
return 0;
else asn1_cb = 0;
{
case ASN1_ITYPE_PRIMITIVE:
{
/* tagging or OPTIONAL is currently illegal on an item
* template because the flags can't get passed down.
* In practice this isn't a problem: we include the
* relevant flags from the item template in the
* template itself.
*/
{
goto err;
}
}
break;
case ASN1_ITYPE_MSTRING:
p = *in;
/* Just read in tag and class */
if (!ret)
{
goto err;
}
/* Must be UNIVERSAL class */
if (oclass != V_ASN1_UNIVERSAL)
{
/* If OPTIONAL, assume this is OK */
if (opt) return -1;
goto err;
}
/* Check tag matches bit map */
{
/* If OPTIONAL, assume this is OK */
if (opt)
return -1;
goto err;
}
case ASN1_ITYPE_EXTERN:
/* Use new style d2i */
case ASN1_ITYPE_COMPAT:
/* we must resort to old style evil hackery */
/* If OPTIONAL see if it is there */
if (opt)
{
int exptag;
p = *in;
if (tag == -1)
/* Don't care about anything other than presence
* of expected tag */
if (!ret)
{
goto err;
}
if (ret == -1)
return -1;
}
/* This is the old style evil hack IMPLICIT handling:
* since the underlying code is expecting a tag and
* class other than the one present we change the
* buffer temporarily then change it back afterwards.
* This doesn't and never did work for tags > 30.
*
* Yes this is *horrible* but it is only needed for
* old style d2i which will hopefully not be around
* for much longer.
* FIXME: should copy the buffer then modify it so
* the input buffer can be const: we should *always*
* copy because the old style d2i might modify the
* buffer.
*/
if (tag != -1)
{
*wp = (unsigned char)((*p & V_ASN1_CONSTRUCTED)
}
if (tag != -1)
if (ptmpval)
return 1;
goto err;
case ASN1_ITYPE_CHOICE:
goto auxerr;
/* Allocate structure */
{
goto err;
}
/* CHOICE type, try each possibility in turn */
p = *in;
{
/* We mark field as OPTIONAL so its absence
* can be recognised.
*/
/* If field not present, try the next one */
if (ret == -1)
continue;
/* If positive return, read OK, break loop */
if (ret > 0)
break;
/* Otherwise must be an ASN1 parsing error */
goto err;
}
/* Did we fall off the end without reading anything? */
{
/* If OPTIONAL, this is OK */
if (opt)
{
/* Free and zero it */
return -1;
}
goto err;
}
*in = p;
goto auxerr;
return 1;
case ASN1_ITYPE_NDEF_SEQUENCE:
case ASN1_ITYPE_SEQUENCE:
p = *in;
/* If no IMPLICIT tagging set to SEQUENCE, UNIVERSAL */
if (tag == -1)
{
}
/* Get SEQUENCE length and update len, p */
if (!ret)
{
goto err;
}
else if (ret == -1)
return -1;
{
seq_nolen = 1;
}
/* If indefinite we don't do a length check */
if (!cst)
{
goto err;
}
{
goto err;
}
goto auxerr;
/* Get each field entry */
{
const ASN1_TEMPLATE *seqtt;
if (!seqtt)
goto err;
/* Have we ran out of data? */
if (!len)
break;
q = p;
if (asn1_check_eoc(&p, len))
{
if (!seq_eoc)
{
goto err;
}
len -= p - q;
seq_eoc = 0;
q = p;
break;
}
/* This determines the OPTIONAL flag value. The field
* cannot be omitted if it is the last of a SEQUENCE
* and there is still data to be read. This isn't
* strictly necessary but it increases efficiency in
* some cases.
*/
isopt = 0;
/* attempt to read in field, allowing each to be
* OPTIONAL */
if (!ret)
{
goto err;
}
else if (ret == -1)
{
/* OPTIONAL component absent.
* Free and zero the field.
*/
continue;
}
/* Update length */
len -= p - q;
}
/* Check for EOC if expecting one */
{
goto err;
}
/* Check all data read */
{
goto err;
}
/* If we get here we've got no more data in the SEQUENCE,
* however we may not have read all fields so check all
* remaining are OPTIONAL and clear any that are.
*/
{
const ASN1_TEMPLATE *seqtt;
if (!seqtt)
goto err;
{
}
else
{
goto err;
}
}
/* Save encoding */
goto auxerr;
*in = p;
goto auxerr;
return 1;
default:
return 0;
}
err:
if (errtt)
else
return 0;
}
/* Templates are handled with two separate functions.
* One handles any EXPLICIT tag and the other handles the rest.
*/
{
int ret;
long len;
const unsigned char *p, *q;
char exp_eoc;
if (!val)
return 0;
p = *in;
/* Check if EXPLICIT tag expected */
if (flags & ASN1_TFLG_EXPTAG)
{
char cst;
/* Need to work out amount of data available to the inner
* content and where it starts: so read in EXPLICIT header to
* get the info.
*/
q = p;
if (!ret)
{
return 0;
}
else if (ret == -1)
return -1;
if (!cst)
{
return 0;
}
/* We've found the field so it can't be OPTIONAL now */
if (!ret)
{
return 0;
}
/* We read the field in OK so update length */
len -= p - q;
if (exp_eoc)
{
/* If NDEF we must have an EOC here */
if (!asn1_check_eoc(&p, len))
{
goto err;
}
}
else
{
/* Otherwise we must hit the EXPLICIT tag end or its
* an error */
if (len)
{
goto err;
}
}
}
else
*in = p;
return 1;
err:
return 0;
}
{
int ret;
const unsigned char *p, *q;
if (!val)
return 0;
p = *in;
q = p;
if (flags & ASN1_TFLG_SK_MASK)
{
/* SET OF, SEQUENCE OF */
char sk_eoc;
/* First work out expected inner tag value */
if (flags & ASN1_TFLG_IMPTAG)
{
}
else
{
if (flags & ASN1_TFLG_SET_OF)
sktag = V_ASN1_SET;
else
}
/* Get the tag */
if (!ret)
{
return 0;
}
else if (ret == -1)
return -1;
if (!*val)
else
{
/* We've got a valid STACK: free up any items present */
{
}
}
if (!*val)
{
goto err;
}
/* Read as many items as we can */
while(len > 0)
{
q = p;
/* See if EOC found */
if (asn1_check_eoc(&p, len))
{
if (!sk_eoc)
{
goto err;
}
len -= p - q;
sk_eoc = 0;
break;
}
-1, 0, 0, ctx))
{
goto err;
}
len -= p - q;
{
goto err;
}
}
if (sk_eoc)
{
goto err;
}
}
else if (flags & ASN1_TFLG_IMPTAG)
{
/* IMPLICIT tagging */
if (!ret)
{
goto err;
}
else if (ret == -1)
return -1;
}
else
{
/* Nothing special */
if (!ret)
{
goto err;
}
else if (ret == -1)
return -1;
}
*in = p;
return 1;
err:
return 0;
}
{
long plen;
const unsigned char *p;
long len;
if (!pval)
{
return 0; /* Should never happen */
}
{
tag = -1;
}
else
if (utype == V_ASN1_ANY)
{
/* If type is ANY need to figure out type from tag */
unsigned char oclass;
if (tag >= 0)
{
return 0;
}
if (opt)
{
return 0;
}
p = *in;
if (!ret)
{
return 0;
}
if (oclass != V_ASN1_UNIVERSAL)
}
if (tag == -1)
{
}
p = *in;
/* Check header */
if (!ret)
{
return 0;
}
else if (ret == -1)
return -1;
/* SEQUENCE, SET and "OTHER" are left in encoded form */
if ((utype == V_ASN1_SEQUENCE)
{
/* Clear context cache for type OTHER because the auto clear
* when we have a exact match wont work
*/
if (utype == V_ASN1_OTHER)
{
}
/* SEQUENCE and SET must be constructed */
else if (!cst)
{
return 0;
}
/* If indefinite length constructed find the real end */
if (inf)
{
goto err;
}
else
{
p += plen;
}
}
else if (cst)
{
/* Should really check the internal tags are correct but
* some things may get this wrong. The relevant specs
* say that constructed string types should be OCTET STRINGs
* internally irrespective of the type. So instead just check
* for UNIVERSAL class and ignore the tag.
*/
goto err;
/* Append a final null to string */
{
return 0;
}
free_cont = 1;
}
else
{
cont = p;
p += plen;
}
/* We now have content length and type: translate into a structure */
goto err;
*in = p;
ret = 1;
err:
return ret;
}
/* Translate ASN1 content octets into a structure */
{
int ret = 0;
const ASN1_PRIMITIVE_FUNCS *pf;
ASN1_INTEGER **tint;
/* If ANY type clear type and set pointer to internal value */
{
if (!*pval)
{
typ = ASN1_TYPE_new();
}
else
}
switch(utype)
{
case V_ASN1_OBJECT:
goto err;
break;
case V_ASN1_NULL:
if (len)
{
goto err;
}
break;
case V_ASN1_BOOLEAN:
if (len != 1)
{
goto err;
}
else
{
}
break;
case V_ASN1_BIT_STRING:
goto err;
break;
case V_ASN1_INTEGER:
case V_ASN1_NEG_INTEGER:
case V_ASN1_ENUMERATED:
case V_ASN1_NEG_ENUMERATED:
goto err;
/* Fixup type to match the expected form */
break;
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_OTHER:
case V_ASN1_SET:
case V_ASN1_SEQUENCE:
default:
/* All based on ASN1_STRING and handled the same */
if (!*pval)
{
if (!stmp)
{
goto err;
}
}
else
{
}
/* If we've already allocated a buffer use it */
if (*free_cont)
{
*free_cont = 0;
}
else
{
{
goto err;
}
}
break;
}
/* If ASN1_ANY and NULL type fix up value */
ret = 1;
err:
if (!ret)
{
if (opval)
}
return ret;
}
/* This function finds the end of an ASN1 structure when passed its maximum
* length, whether it is indefinite length and a pointer to the content.
* This is more efficient than calling asn1_collect because it does not
* recurse on each indefinite length header.
*/
{
int expected_eoc;
long plen;
const unsigned char *p = *in, *q;
/* If not indefinite length constructed just add length */
if (inf == 0)
{
return 1;
}
expected_eoc = 1;
/* Indefinite length constructed form. Find the end when enough EOCs
* are found. If more indefinite length constructed headers
* are encountered increment the expected eoc count otherwise just
* skip to the end of the data.
*/
while (len > 0)
{
if(asn1_check_eoc(&p, len))
{
expected_eoc--;
if (expected_eoc == 0)
break;
len -= 2;
continue;
}
q = p;
/* Just read in a header: only care about the length */
-1, 0, 0, NULL))
{
return 0;
}
if (inf)
expected_eoc++;
else
p += plen;
len -= p - q;
}
if (expected_eoc)
{
return 0;
}
*in = p;
return 1;
}
/* This function collects the asn1 data from a constructred string
* type into a buffer. The values of 'in' and 'len' should refer
* to the contents of the constructed type and 'inf' should be set
* if it is indefinite length.
*/
{
const unsigned char *p, *q;
long plen;
p = *in;
inf &= 1;
/* If no buffer and not indefinite length constructed just pass over
* the encoded data */
{
return 1;
}
while(len > 0)
{
q = p;
/* Check for EOC */
if (asn1_check_eoc(&p, len))
{
/* EOC is illegal outside indefinite length
* constructed form */
if (!inf)
{
return 0;
}
inf = 0;
break;
}
{
return 0;
}
/* If indefinite length constructed update max length */
if (cst)
{
return 0;
#else
return 0;
#endif
}
return 0;
len -= p - q;
}
if (inf)
{
return 0;
}
*in = p;
return 1;
}
{
int len;
if (buf)
{
{
return 0;
}
}
*p += plen;
return 1;
}
/* Check for ASN1 EOC and swallow it if found */
{
const unsigned char *p;
if (len < 2) return 0;
p = *in;
if (!p[0] && !p[1])
{
*in += 2;
return 1;
}
return 0;
}
/* Check an ASN1 tag and length: a bit like ASN1_get_object
* but it sets the length for indefinite length constructed
* form, we don't know the exact length but we can set an
* upper bound to the amount of data available minus the
* header length just read.
*/
{
int i;
long plen;
const unsigned char *p, *q;
p = *in;
q = p;
{
}
else
{
if (ctx)
{
/* If definite length, and no error, length +
* header can't exceed total amount of data available.
*/
{
return 0;
}
}
}
if (i & 0x80)
{
return 0;
}
if (exptag >= 0)
{
{
/* If type is OPTIONAL, not an error:
* indicate missing type.
*/
if (opt) return -1;
return 0;
}
/* We have a tag and class match:
* assume we are going to do something with it */
}
if (i & 1)
if (inf)
*inf = i & 1;
if (cst)
*cst = i & V_ASN1_CONSTRUCTED;
if (olen)
if (oclass)
if (otag)
*in = p;
return 1;
}