/* * 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 #include #include #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 #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 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 ; itemDexoptions; 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 */