iBoot/lib/pki/libDER/DER_Encode.c

343 lines
8.0 KiB
C
Raw Permalink Normal View History

2023-07-08 13:03:17 -07:00
/* Copyright (c) 2005-2007 Apple Inc. All Rights Reserved. */
/*
* DER_Encode.h - DER encoding routines
*
* Created Dec. 2 2005 by dmitch
*/
#include <libDER/DER_Encode.h>
#include <libDER/asn1Types.h>
#include <libDER/libDER_config.h>
#include <libDER/DER_Decode.h>
#ifndef DER_ENCODE_ENABLE
#error Please define DER_ENCODE_ENABLE.
#endif
#if DER_ENCODE_ENABLE
/* calculate size of encoded tag */
static DERSize DERLengthOfTag(
DERTag tag)
{
DERSize rtn = 1;
tag &= ASN1_TAGNUM_MASK;
if (tag >= 0x1F) {
/* Shift 7-bit digits out of the tag integer until it's zero. */
while(tag != 0) {
rtn++;
tag >>= 7;
}
}
return rtn;
}
/* encode tag */
static DERReturn DEREncodeTag(
DERTag tag,
DERByte *buf, /* encoded length goes here */
DERSize *inOutLen) /* IN/OUT */
{
DERSize outLen = DERLengthOfTag(tag);
DERTag tagNumber = tag & ASN1_TAGNUM_MASK;
DERByte tag1 = (tag >> (sizeof(DERTag) * 8 - 8)) & 0xE0;
if(outLen > *inOutLen) {
return DR_BufOverflow;
}
if(outLen == 1) {
/* short form */
*buf = tag1 | tagNumber;
}
else {
/* long form */
DERByte *tagBytes = buf + outLen; // l.s. digit of tag
*buf = tag1 | 0x1F; // tag class / method indicator
*--tagBytes = tagNumber & 0x7F;
tagNumber >>= 7;
while(tagNumber != 0) {
*--tagBytes = (tagNumber & 0x7F) | 0x80;
tagNumber >>= 7;
}
}
*inOutLen = outLen;
return DR_Success;
}
/* calculate size of encoded length */
DERSize DERLengthOfLength(
DERSize length)
{
DERSize rtn;
if(length < 0x80) {
/* short form length */
return 1;
}
/* long form - one length-of-length byte plus length bytes */
rtn = 1;
while(length != 0) {
rtn++;
length >>= 8;
}
return rtn;
}
/* encode length */
DERReturn DEREncodeLength(
DERSize length,
DERByte *buf, /* encoded length goes here */
DERSize *inOutLen) /* IN/OUT */
{
DERByte *lenBytes;
DERSize outLen = DERLengthOfLength(length);
if(outLen > *inOutLen) {
return DR_BufOverflow;
}
if(length < 0x80) {
/* short form */
*buf = (DERByte)length;
*inOutLen = 1;
return DR_Success;
}
/* long form */
*buf = (outLen - 1) | 0x80; // length of length, long form indicator
lenBytes = buf + outLen - 1; // l.s. digit of length
while(length != 0) {
*lenBytes-- = (DERByte)length;
length >>= 8;
}
*inOutLen = outLen;
return DR_Success;
}
DERSize DERLengthOfItem(
DERTag tag,
DERSize length)
{
return DERLengthOfTag(tag) + DERLengthOfLength(length) + length;
}
DERReturn DEREncodeItem(
DERTag tag,
DERSize length,
const DERByte *src,
DERByte *derOut, /* encoded item goes here */
DERSize *inOutLen) /* IN/OUT */
{
DERReturn drtn;
DERSize itemLen;
DERByte *currPtr = derOut;
DERSize bytesLeft = DERLengthOfItem(tag, length);
if(bytesLeft > *inOutLen) {
return DR_BufOverflow;
}
*inOutLen = bytesLeft;
/* top level tag */
itemLen = bytesLeft;
drtn = DEREncodeTag(tag, currPtr, &itemLen);
if(drtn) {
return drtn;
}
currPtr += itemLen;
bytesLeft -= itemLen;
itemLen = bytesLeft;
drtn = DEREncodeLength(length, currPtr, &itemLen);
if(drtn) {
return drtn;
}
currPtr += itemLen;
bytesLeft -= itemLen;
DERMemmove(currPtr, src, length);
return DR_Success;
}
static /* calculate the content length of an encoded sequence */
DERSize DERContentLengthOfEncodedSequence(
const void *src, /* generally a ptr to a struct full of
* DERItems */
DERShort numItems, /* size of itemSpecs[] */
const DERItemSpec *itemSpecs)
{
DERSize contentLen = 0;
unsigned dex;
DERSize thisContentLen;
/* find length of each item */
for(dex=0; dex<numItems; dex++) {
const DERItemSpec *currItemSpec = &itemSpecs[dex];
DERShort currOptions = currItemSpec->options;
const DERByte *byteSrc = (const DERByte *)src + currItemSpec->offset;
const DERItem *itemSrc = (const DERItem *)byteSrc;
if(currOptions & DER_ENC_WRITE_DER) {
/* easy case - no encode */
contentLen += itemSrc->length;
continue;
}
if ((currOptions & DER_DEC_OPTIONAL) && itemSrc->length == 0) {
/* If an optional item isn't present we don't encode a
tag and len. */
continue;
}
/*
* length of this item =
* tag (one byte) +
* length of length +
* content length +
* optional zero byte for signed integer
*/
contentLen += DERLengthOfTag(currItemSpec->tag);
/* check need for pad byte before calculating lengthOfLength... */
thisContentLen = itemSrc->length;
if((currOptions & DER_ENC_SIGNED_INT) &&
(itemSrc->length != 0)) {
if(itemSrc->data[0] & 0x80) {
/* insert zero keep it positive */
thisContentLen++;
}
}
contentLen += DERLengthOfLength(thisContentLen);
contentLen += thisContentLen;
}
return contentLen;
}
DERReturn DEREncodeSequence(
DERTag topTag, /* ASN1_CONSTR_SEQUENCE, ASN1_CONSTR_SET */
const void *src, /* generally a ptr to a struct full of
* DERItems */
DERShort numItems, /* size of itemSpecs[] */
const DERItemSpec *itemSpecs,
DERByte *derOut, /* encoded data written here */
DERSize *inOutLen) /* IN/OUT */
{
const DERByte *endPtr = derOut + *inOutLen;
DERByte *currPtr = derOut;
DERSize bytesLeft = *inOutLen;
DERSize contentLen;
DERReturn drtn;
DERSize itemLen;
unsigned dex;
/* top level tag */
itemLen = bytesLeft;
drtn = DEREncodeTag(topTag, currPtr, &itemLen);
if(drtn) {
return drtn;
}
currPtr += itemLen;
bytesLeft -= itemLen;
if(currPtr >= endPtr) {
return DR_BufOverflow;
}
/* content length */
contentLen = DERContentLengthOfEncodedSequence(src, numItems, itemSpecs);
itemLen = bytesLeft;
drtn = DEREncodeLength(contentLen, currPtr, &itemLen);
if(drtn) {
return drtn;
}
currPtr += itemLen;
bytesLeft -= itemLen;
if(currPtr + contentLen > endPtr) {
return DR_BufOverflow;
}
/* we don't have to check for overflow any more */
/* grind thru the items */
for(dex=0; dex<numItems; dex++) {
const DERItemSpec *currItemSpec = &itemSpecs[dex];
DERShort currOptions = currItemSpec->options;
const DERByte *byteSrc = (const DERByte *)src + currItemSpec->offset;
const DERItem *itemSrc = (const DERItem *)byteSrc;
int prependZero = 0;
if(currOptions & DER_ENC_WRITE_DER) {
/* easy case */
DERMemmove(currPtr, itemSrc->data, itemSrc->length);
currPtr += itemSrc->length;
bytesLeft -= itemSrc->length;
continue;
}
if ((currOptions & DER_DEC_OPTIONAL) && itemSrc->length == 0) {
/* If an optional item isn't present we skip it. */
continue;
}
/* encode one item: first the tag */
itemLen = bytesLeft;
drtn = DEREncodeTag(currItemSpec->tag, currPtr, &itemLen);
if(drtn) {
return drtn;
}
currPtr += itemLen;
bytesLeft -= itemLen;
/* do we need to prepend a zero to content? */
contentLen = itemSrc->length;
if((currOptions & DER_ENC_SIGNED_INT) &&
(itemSrc->length != 0)) {
if(itemSrc->data[0] & 0x80) {
/* insert zero keep it positive */
contentLen++;
prependZero = 1;
}
}
/* encode content length */
itemLen = bytesLeft;
drtn = DEREncodeLength(contentLen, currPtr, &itemLen);
if(drtn) {
return drtn;
}
currPtr += itemLen;
bytesLeft -= itemLen;
/* now the content, with possible leading zero added */
if(prependZero) {
*currPtr++ = 0;
bytesLeft--;
}
DERMemmove(currPtr, itemSrc->data, itemSrc->length);
currPtr += itemSrc->length;
bytesLeft -= itemSrc->length;
}
*inOutLen = (DERSize)(currPtr - derOut);
return DR_Success;
}
/* calculate the length of an encoded sequence. */
DERSize DERLengthOfEncodedSequence(
DERTag topTag,
const void *src, /* generally a ptr to a struct full of
* DERItems */
DERShort numItems, /* size of itemSpecs[] */
const DERItemSpec *itemSpecs)
{
DERSize contentLen = DERContentLengthOfEncodedSequence(
src, numItems, itemSpecs);
return DERLengthOfTag(topTag) +
DERLengthOfLength(contentLen) +
contentLen;
}
#endif /* DER_ENCODE_ENABLE */