343 lines
8.0 KiB
C
343 lines
8.0 KiB
C
|
/* 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 */
|
||
|
|