iBoot/lib/pki/libDER/DER_Decode.c

646 lines
18 KiB
C
Raw Permalink Normal View History

2023-07-08 13:03:17 -07:00
/*
* Copyright (c) 2005-2010 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* DER_Decode.c - DER decoding routines
*/
#include <libDER/DER_Decode.h>
#include <libDER/asn1Types.h>
#include <libDER/libDER_config.h>
#ifndef DER_DECODE_ENABLE
#error Please define DER_DECODE_ENABLE.
#endif
#if DER_DECODE_ENABLE
#define DER_DECODE_DEBUG 0
#if DER_DECODE_DEBUG
#include <stdio.h>
#define derDecDbg(a) printf(a)
#define derDecDbg1(a, b) printf(a, b)
#define derDecDbg2(a, b, c) printf(a, b, c)
#define derDecDbg3(a, b, c, d) printf(a, b, c, d)
#else
#define derDecDbg(a)
#define derDecDbg1(a, b)
#define derDecDbg2(a, b, c)
#define derDecDbg3(a, b, c, d)
#endif /* DER_DECODE_DEBUG */
/*
* Basic decoding primitive. Only works with:
*
* -- definite length encoding
* -- one-byte tags (unless DER_MULTIBYTE_TAGS is defined)
* -- max content length fits in a DERSize
*
* No malloc or copy of the contents is performed; the returned
* content->content.data is a pointer into the incoming der data.
*/
DERReturn DERDecodeItem(
const DERItem *der, /* data to decode */
DERDecodedInfo *decoded) /* RETURNED */
{
return DERDecodeItemPartialBuffer(der, decoded, false);
}
/*
* Basic decoding primitive. Allows for decoding with a partial buffer.
* if allowPartialBuffer is true. A partial buffer would normally fail
* because the encoded length would be greater than the size of the buffer passed in.
* Only works with:
*
* -- definite length encoding
* -- one-byte tags (unless DER_MULTIBYTE_TAGS is defined)
* -- max content length fits in a DERSize
*
* No malloc or copy of the contents is performed; the returned
* content->content.data is a pointer into the incoming der data.
*/
DERReturn DERDecodeItemPartialBuffer(
const DERItem *der, /* data to decode */
DERDecodedInfo *decoded, /* RETURNED */
bool allowPartialBuffer)
{
DERByte tag1; /* first tag byte */
DERByte len1; /* first length byte */
DERTag tagNumber; /* tag number without class and method bits */
DERByte *derPtr = der->data;
DERSize derLen = der->length;
/* The tag decoding below is fully BER complient. We support a max tag
value of 2 ^ ((sizeof(DERTag) * 8) - 3) - 1 so for tag size 1 byte we
support tag values from 0 - 0x1F. For tag size 2 tag values
from 0 - 0x1FFF and for tag size 4 values from 0 - 0x1FFFFFFF. */
if(derLen < 2) {
return DR_DecodeError;
}
/* Grab the first byte of the tag. */
tag1 = *derPtr++;
derLen--;
tagNumber = tag1 & 0x1F;
if(tagNumber == 0x1F) {
#ifdef DER_MULTIBYTE_TAGS
/* Long tag form: bit 8 of each octet shall be set to one unless it is
the last octet of the tag */
const DERTag overflowMask = ((DERTag)0x7F << (sizeof(DERTag) * 8 - 7));
DERByte tagByte;
tagNumber = 0;
if (*derPtr == 0x80 || *derPtr < 0x1F)
return DR_DecodeError;
do {
if(derLen < 2 || (tagNumber & overflowMask) != 0) {
return DR_DecodeError;
}
tagByte = *derPtr++;
derLen--;
tagNumber = (tagNumber << 7) | (tagByte & 0x7F);
} while((tagByte & 0x80) != 0);
/* Check for any of the top 3 reserved bits being set. */
if ((tagNumber & (overflowMask << 4)) != 0)
#endif
return DR_DecodeError;
}
/* Returned tag, top 3 bits are class/method remaining bits are number. */
decoded->tag = ((DERTag)(tag1 & 0xE0) << ((sizeof(DERTag) - 1) * 8)) | tagNumber;
/* Tag decoding above ensured we have at least one more input byte left. */
len1 = *derPtr++;
derLen--;
if(len1 & 0x80) {
/* long length form - first byte is length of length */
DERSize longLen = 0; /* long form length */
unsigned dex;
len1 &= 0x7f;
if((len1 > sizeof(DERSize)) || (len1 > derLen) || len1 == 0 || *derPtr == 0) {
/* no can do */
return DR_DecodeError;
}
for(dex=0; dex<len1; dex++) {
longLen <<= 8;
longLen |= *derPtr++;
derLen--;
}
if(longLen > derLen && !allowPartialBuffer) {
/* not enough data left for this encoding */
return DR_DecodeError;
}
decoded->content.data = derPtr;
decoded->content.length = longLen;
}
else {
/* short length form, len1 is the length */
if(len1 > derLen && !allowPartialBuffer) {
/* not enough data left for this encoding */
return DR_DecodeError;
}
decoded->content.data = derPtr;
decoded->content.length = len1;
}
return DR_Success;
}
/*
* Given a BIT_STRING, in the form of its raw content bytes,
* obtain the number of unused bits and the raw bit string bytes.
*/
DERReturn DERParseBitString(
const DERItem *contents,
DERItem *bitStringBytes, /* RETURNED */
DERByte *numUnusedBits) /* RETURNED */
{
if(contents->length < 2) {
/* not enough room for actual bits after the unused bits field */
*numUnusedBits = 0;
bitStringBytes->data = NULL;
bitStringBytes->length = 0;
return DR_Success;
}
*numUnusedBits = contents->data[0];
bitStringBytes->data = contents->data + 1;
bitStringBytes->length = contents->length - 1;
return DR_Success;
}
/*
* Given a BOOLEAN, in the form of its raw content bytes,
* obtain it's value.
*/
DERReturn DERParseBoolean(
const DERItem *contents,
bool *value) { /* RETURNED */
if (contents->length != 1 ||
(contents->data[0] != 0 && contents->data[0] != 0xFF))
return DR_DecodeError;
*value = contents->data[0] != 0;
return DR_Success;
}
/*
* Given a BOOLEAN, in the form of its raw content bytes,
* obtain it's value.
*/
DERReturn DERParseBooleanWithDefault(
const DERItem *contents,
bool defaultValue,
bool *value) { /* RETURNED */
if (contents->length == 0) {
*value = defaultValue;
return DR_Success;
}
return DERParseBoolean(contents, value);
}
DERReturn DERParseInteger(
const DERItem *contents,
uint32_t *result) { /* RETURNED */
uint64_t value;
DERReturn drtn = DERParseInteger64(contents, &value);
if (drtn == DR_Success) {
if (value > UINT32_MAX)
drtn = DR_BufOverflow;
else
*result = (uint32_t)value;
}
return drtn;
}
DERReturn DERParseInteger64(
const DERItem *contents,
uint64_t *result) { /* RETURNED */
DERSize ix, length = contents->length;
if (length == 0)
return DR_DecodeError;
if (contents->data[0] & 0x80)
return DR_DecodeError;
if (contents->data[0] == 0) {
if (length > 1 && (contents->data[1] & 0x80) == 0)
return DR_DecodeError;
if (length > sizeof(*result) + 1)
return DR_BufOverflow;
} else if (length > sizeof(*result)) {
return DR_BufOverflow;
}
uint64_t value = 0;
for (ix = 0; ix < length; ++ix) {
value <<= 8;
value += contents->data[ix];
}
*result = value;
return DR_Success;
}
/* Sequence/set support */
/*
* To decode a set or sequence, call DERDecodeSeqInit once, then
* call DERDecodeSeqNext to get each enclosed item.
* DERDecodeSeqNext returns DR_EndOfSequence when no more
* items are available.
*/
DERReturn DERDecodeSeqInit(
const DERItem *der, /* data to decode */
DERTag *tag, /* RETURNED tag of sequence/set. This will be
* either ASN1_CONSTR_SEQUENCE or ASN1_CONSTR_SET. */
DERSequence *derSeq) /* RETURNED, to use in DERDecodeSeqNext */
{
DERDecodedInfo decoded;
DERReturn drtn;
drtn = DERDecodeItem(der, &decoded);
if(drtn) {
return drtn;
}
*tag = decoded.tag;
switch(decoded.tag) {
case ASN1_CONSTR_SEQUENCE:
case ASN1_CONSTR_SET:
break;
default:
return DR_UnexpectedTag;
}
derSeq->nextItem = decoded.content.data;
derSeq->end = decoded.content.data + decoded.content.length;
return DR_Success;
}
/*
* Use this to start in on decoding a sequence's content, when
* the top-level tag and content have already been decoded.
*/
DERReturn DERDecodeSeqContentInit(
const DERItem *content,
DERSequence *derSeq) /* RETURNED, to use in DERDecodeSeqNext */
{
/* just prepare for decoding items in content */
derSeq->nextItem = content->data;
derSeq->end = content->data + content->length;
return DR_Success;
}
DERReturn DERDecodeSeqNext(
DERSequence *derSeq,
DERDecodedInfo *decoded) /* RETURNED */
{
DERReturn drtn;
DERItem item;
if(derSeq->nextItem >= derSeq->end) {
/* normal termination, contents all used up */
return DR_EndOfSequence;
}
/* decode next item */
item.data = derSeq->nextItem;
item.length = (DERSize)(derSeq->end - derSeq->nextItem);
drtn = DERDecodeItem(&item, decoded);
if(drtn) {
return drtn;
}
/* skip over the item we just decoded */
derSeq->nextItem = decoded->content.data + decoded->content.length;
return DR_Success;
}
/*
* High level sequence parse, starting with top-level tag and content.
* Top level tag must be ASN1_CONSTR_SEQUENCE - if it's not, and that's
* OK, use DERParseSequenceContent().
*/
DERReturn DERParseSequence(
const DERItem *der,
DERShort numItems, /* size of itemSpecs[] */
const DERItemSpec *itemSpecs,
void *dest, /* DERDecodedInfo(s) here RETURNED */
DERSize sizeToZero) /* optional */
{
DERReturn drtn;
DERDecodedInfo topDecode;
drtn = DERDecodeItem(der, &topDecode);
if(drtn) {
return drtn;
}
if(topDecode.tag != ASN1_CONSTR_SEQUENCE) {
return DR_UnexpectedTag;
}
return DERParseSequenceContent(&topDecode.content,
numItems, itemSpecs, dest, sizeToZero);
}
/* high level sequence parse, starting with sequence's content */
DERReturn DERParseSequenceContent(
const DERItem *content,
DERShort numItems, /* size of itemSpecs[] */
const DERItemSpec *itemSpecs,
void *dest, /* DERDecodedInfo(s) here RETURNED */
DERSize sizeToZero) /* optional */
{
DERSequence derSeq;
DERReturn drtn;
DERShort itemDex;
DERByte *currDER; /* full DER encoding of current item */
if(sizeToZero) {
DERMemset(dest, 0, sizeToZero);
}
drtn = DERDecodeSeqContentInit(content, &derSeq);
if(drtn) {
return drtn;
}
/* main loop */
for(itemDex=0 ; itemDex<numItems; ) {
DERDecodedInfo currDecoded;
DERShort i;
DERTag foundTag;
char foundMatch = 0;
/* save this in case of DER_DEC_SAVE_DER */
currDER = derSeq.nextItem;
drtn = DERDecodeSeqNext(&derSeq, &currDecoded);
if(drtn) {
/*
* One legal error here is DR_EndOfSequence when
* all remaining DERSequenceItems are optional.
*/
if(drtn == DR_EndOfSequence) {
for(i=itemDex; i<numItems; i++) {
if(!(itemSpecs[i].options & DER_DEC_OPTIONAL)) {
/* unexpected end of sequence */
return DR_IncompleteSeq;
}
}
/* the rest are optional; success */
return DR_Success;
}
else {
/* any other error is fatal */
return drtn;
}
} /* decode error */
/*
* Seek matching tag or ASN_ANY in itemSpecs, skipping
* over optional items.
*/
foundTag = currDecoded.tag;
derDecDbg1("--- foundTag 0x%llx\n", foundTag);
for(i=itemDex; i<numItems; i++) {
const DERItemSpec *currItemSpec = &itemSpecs[i];
DERShort currOptions = currItemSpec->options;
derDecDbg3("--- currItem %u expectTag 0x%llx currOptions 0x%x\n",
i, currItemSpec->tag, currOptions);
if((currOptions & DER_DEC_ASN_ANY) ||
(foundTag == currItemSpec->tag)) {
/*
* We're good with this one. Cook up destination address
* as appropriate.
*/
if(!(currOptions & DER_DEC_SKIP)) {
derDecDbg1("--- MATCH at currItem %u\n", i);
DERByte *byteDst = (DERByte *)dest + currItemSpec->offset;
DERItem *dst = (DERItem *)byteDst;
*dst = currDecoded.content;
if(currOptions & DER_DEC_SAVE_DER) {
/* recreate full DER encoding of this item */
derDecDbg1("--- SAVE_DER at currItem %u\n", i);
dst->data = currDER;
dst->length += (currDecoded.content.data - currDER);
}
}
/* on to next item */
itemDex = i + 1;
/* is this the end? */
if(itemDex == numItems) {
/* normal termination if we consumed everything */
if (derSeq.nextItem == derSeq.end)
return DR_Success;
else
return DR_DecodeError;
}
else {
/* on to next item */
foundMatch = 1;
break;
}
} /* ASN_ANY, or match */
/*
* If current itemSpec isn't optional, abort - else on to
* next item
*/
if(!(currOptions & DER_DEC_OPTIONAL)) {
derDecDbg1("--- MISMATCH at currItem %u, !OPTIONAL, abort\n", i);
return DR_UnexpectedTag;
}
/* else this was optional, on to next item */
} /* searching for tag match */
if(foundMatch == 0) {
/*
* Found an item we couldn't match to any tag spec and we're at
* the end.
*/
derDecDbg("--- TAG NOT FOUND, abort\n");
return DR_UnexpectedTag;
}
/* else on to next item */
} /* main loop */
/* Template has 0 items if we get here. */
/* normal termination if we consumed everything, (the sequence was empty) */
if (derSeq.nextItem == derSeq.end)
return DR_Success;
else
return DR_DecodeError;
}
#if 0
/*
* High level sequence parse, starting with top-level tag and content.
* Top level tag must be ASN1_CONSTR_SEQUENCE - if it's not, and that's
* OK, use DERParseSequenceContent().
*/
DERReturn DERParseSequenceOf(
const DERItem *der,
DERShort numItems, /* size of itemSpecs[] */
const DERItemSpec *itemSpecs,
void *dest, /* DERDecodedInfo(s) here RETURNED */
DERSize *numDestItems) /* output */
{
DERReturn drtn;
DERDecodedInfo topDecode;
drtn = DERDecodeItem(der, &topDecode);
if(drtn) {
return drtn;
}
if(topDecode.tag != ASN1_CONSTR_SEQUENCE) {
return DR_UnexpectedTag;
}
return DERParseSequenceContent(&topDecode.content,
numItems, itemSpecs, dest, sizeToZero);
}
/*
* High level set of parse, starting with top-level tag and content.
* Top level tag must be ASN1_CONSTR_SET - if it's not, and that's
* OK, use DERParseSetOrSequenceOfContent().
*/
DERReturn DERParseSetOf(
const DERItem *der,
DERShort numItems, /* size of itemSpecs[] */
const DERItemSpec *itemSpecs,
void *dest, /* DERDecodedInfo(s) here RETURNED */
DERSize *numDestItems) /* output */
{
DERReturn drtn;
DERDecodedInfo topDecode;
drtn = DERDecodeItem(der, &topDecode);
if(drtn) {
return drtn;
}
if(topDecode.tag != ASN1_CONSTR_SET) {
return DR_UnexpectedTag;
}
return DERParseSetOrSequenceOfContent(&topDecode.content,
numItems, itemSpecs, dest, numDestItems);
}
/* High level set of or sequence of parse, starting with set or
sequence's content */
DERReturn DERParseSetOrSequenceOfContent(
const DERItem *content,
void(*itemHandeler)(void *, const DERDecodedInfo *)
void *itemHandelerContext);
{
DERSequence derSeq;
DERShort itemDex;
drtn = DERDecodeSeqContentInit(content, &derSeq);
require_noerr_quiet(drtn, badCert);
/* main loop */
for (;;) {
DERDecodedInfo currDecoded;
DERShort i;
DERByte foundTag;
char foundMatch = 0;
drtn = DERDecodeSeqNext(&derSeq, &currDecoded);
if(drtn) {
/* The only legal error here is DR_EndOfSequence. */
if(drtn == DR_EndOfSequence) {
/* no more items left in the sequence; success */
return DR_Success;
}
else {
/* any other error is fatal */
require_noerr_quiet(drtn, badCert);
}
} /* decode error */
/* Each element can be anything. */
foundTag = currDecoded.tag;
/*
* We're good with this one. Cook up destination address
* as appropriate.
*/
DERByte *byteDst = (DERByte *)dest + currItemSpec->offset;
DERItem *dst = (DERItem *)byteDst;
*dst = currDecoded.content;
if(currOptions & DER_DEC_SAVE_DER) {
/* recreate full DER encoding of this item */
derDecDbg1("--- SAVE_DER at currItem %u\n", i);
dst->data = currDER;
dst->length += (currDecoded.content.data - currDER);
}
/* on to next item */
itemDex = i + 1;
/* is this the end? */
if(itemDex == numItems) {
/* normal termination */
return DR_Success;
}
else {
/* on to next item */
foundMatch = 1;
break;
}
/*
* If current itemSpec isn't optional, abort - else on to
* next item
*/
if(!(currOptions & DER_DEC_OPTIONAL)) {
derDecDbg1("--- MISMATCH at currItem %u, !OPTIONAL, abort\n", i);
return DR_UnexpectedTag;
}
/* else this was optional, on to next item */
} /* searching for tag match */
if(foundMatch == 0) {
/*
* Found an item we couldn't match to any tag spec and we're at
* the end.
*/
derDecDbg("--- TAG NOT FOUND, abort\n");
return DR_UnexpectedTag;
}
/* else on to next item */
} /* main loop */
/*
* If we get here, there appears to be more to process, but we've
* given the caller everything they want.
*/
return DR_Success;
}
}
#endif
#endif /* DER_DECODE_ENABLE */