1/*
2 * Copyright (c) 2005-2007,2011,2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25/*
26 * DER_Encode.h - DER encoding routines
27 *
28 */
29
30#include <libDER/DER_Encode.h>
31#include <libDER/asn1Types.h>
32#include <libDER/libDER_config.h>
33#include <libDER/DER_Decode.h>
34
35#ifndef	DER_ENCODE_ENABLE
36#error Please define DER_ENCODE_ENABLE.
37#endif
38
39#if		DER_ENCODE_ENABLE
40
41/* calculate size of encoded tag */
42static DERSize DERLengthOfTag(
43	DERTag tag)
44{
45	DERSize rtn = 1;
46
47    tag &= ASN1_TAGNUM_MASK;
48    if (tag >= 0x1F) {
49        /* Shift 7-bit digits out of the tag integer until it's zero. */
50        while(tag != 0) {
51            rtn++;
52            tag >>= 7;
53        }
54    }
55
56    return rtn;
57}
58
59/* encode tag */
60static DERReturn DEREncodeTag(
61	DERTag tag,
62	DERByte *buf,		/* encoded length goes here */
63	DERSize *inOutLen)	/* IN/OUT */
64{
65    DERSize outLen = DERLengthOfTag(tag);
66    DERTag tagNumber = tag & ASN1_TAGNUM_MASK;
67    DERByte tag1 = (tag >> (sizeof(DERTag) * 8 - 8)) & 0xE0;
68
69	if(outLen > *inOutLen) {
70		return DR_BufOverflow;
71	}
72
73    if(outLen == 1) {
74		/* short form */
75		*buf = tag1 | tagNumber;
76	}
77	else {
78        /* long form */
79        DERByte *tagBytes = buf + outLen;	// l.s. digit of tag
80        *buf = tag1 | 0x1F;                 // tag class / method indicator
81        *--tagBytes = tagNumber & 0x7F;
82        tagNumber >>= 7;
83        while(tagNumber != 0) {
84            *--tagBytes = (tagNumber & 0x7F) | 0x80;
85            tagNumber >>= 7;
86        }
87    }
88	*inOutLen = outLen;
89	return DR_Success;
90}
91
92/* calculate size of encoded length */
93DERSize DERLengthOfLength(
94	DERSize length)
95{
96	DERSize rtn;
97
98	if(length < 0x80) {
99		/* short form length */
100		return 1;
101	}
102
103	/* long form - one length-of-length byte plus length bytes */
104	rtn = 1;
105	while(length != 0) {
106		rtn++;
107		length >>= 8;
108	}
109	return rtn;
110}
111
112/* encode length */
113DERReturn DEREncodeLength(
114	DERSize length,
115	DERByte *buf,		/* encoded length goes here */
116	DERSize *inOutLen)	/* IN/OUT */
117{
118	DERByte *lenBytes;
119	DERSize outLen = DERLengthOfLength(length);
120
121	if(outLen > *inOutLen) {
122		return DR_BufOverflow;
123	}
124
125	if(length < 0x80) {
126		/* short form */
127		*buf = (DERByte)length;
128		*inOutLen = 1;
129		return DR_Success;
130	}
131
132	/* long form */
133	*buf = (outLen - 1) | 0x80;		// length of length, long form indicator
134	lenBytes = buf + outLen - 1;	// l.s. digit of length
135	while(length != 0) {
136		*lenBytes-- = (DERByte)length;
137		length >>= 8;
138	}
139	*inOutLen = outLen;
140	return DR_Success;
141}
142
143DERSize DERLengthOfItem(
144	DERTag tag,
145	DERSize length)
146{
147    return DERLengthOfTag(tag) + DERLengthOfLength(length) + length;
148}
149
150DERReturn DEREncodeItem(
151	DERTag tag,
152	DERSize length,
153    const DERByte *src,
154	DERByte *derOut,	/* encoded item goes here */
155	DERSize *inOutLen)	/* IN/OUT */
156{
157	DERReturn		drtn;
158	DERSize			itemLen;
159	DERByte			*currPtr = derOut;
160	DERSize         bytesLeft = DERLengthOfItem(tag, length);
161	if(bytesLeft > *inOutLen) {
162		return DR_BufOverflow;
163	}
164	*inOutLen = bytesLeft;
165
166	/* top level tag */
167	itemLen = bytesLeft;
168	drtn = DEREncodeTag(tag, currPtr, &itemLen);
169	if(drtn) {
170		return drtn;
171	}
172	currPtr += itemLen;
173	bytesLeft -= itemLen;
174	itemLen = bytesLeft;
175	drtn = DEREncodeLength(length, currPtr, &itemLen);
176	if(drtn) {
177		return drtn;
178	}
179	currPtr += itemLen;
180	bytesLeft -= itemLen;
181	DERMemmove(currPtr, src, length);
182
183    (void) bytesLeft;
184
185	return DR_Success;
186}
187
188static /* calculate the content length of an encoded sequence */
189DERSize DERContentLengthOfEncodedSequence(
190	const void			*src,		/* generally a ptr to a struct full of
191									 *    DERItems */
192	DERShort			numItems,	/* size of itemSpecs[] */
193	const DERItemSpec	*itemSpecs)
194{
195	DERSize contentLen = 0;
196	unsigned dex;
197	DERSize thisContentLen;
198
199	/* find length of each item */
200	for(dex=0; dex<numItems; dex++) {
201		const DERItemSpec *currItemSpec = &itemSpecs[dex];
202		DERShort currOptions = currItemSpec->options;
203		const DERByte *byteSrc = (const DERByte *)src + currItemSpec->offset;
204		const DERItem *itemSrc = (const DERItem *)byteSrc;
205
206		if(currOptions & DER_ENC_WRITE_DER) {
207			/* easy case - no encode */
208			contentLen += itemSrc->length;
209			continue;
210		}
211
212        if ((currOptions & DER_DEC_OPTIONAL) && itemSrc->length == 0) {
213            /* If an optional item isn't present we don't encode a
214               tag and len. */
215            continue;
216        }
217
218		/*
219		 * length of this item =
220		 *   tag (one byte) +
221		 *   length of length +
222		 *   content length +
223		 *   optional zero byte for signed integer
224		 */
225		contentLen += DERLengthOfTag(currItemSpec->tag);
226
227		/* check need for pad byte before calculating lengthOfLength... */
228		thisContentLen = itemSrc->length;
229		if((currOptions & DER_ENC_SIGNED_INT) &&
230		   (itemSrc->length != 0)) {
231			if(itemSrc->data[0] & 0x80) {
232				/* insert zero keep it positive */
233				thisContentLen++;
234			}
235		}
236		contentLen += DERLengthOfLength(thisContentLen);
237		contentLen += thisContentLen;
238	}
239	return contentLen;
240}
241
242DERReturn DEREncodeSequence(
243	DERTag				topTag,		/* ASN1_CONSTR_SEQUENCE, ASN1_CONSTR_SET */
244	const void			*src,		/* generally a ptr to a struct full of
245									 *    DERItems */
246	DERShort			numItems,	/* size of itemSpecs[] */
247	const DERItemSpec	*itemSpecs,
248	DERByte				*derOut,	/* encoded data written here */
249	DERSize				*inOutLen)	/* IN/OUT */
250{
251	const DERByte	*endPtr = derOut + *inOutLen;
252	DERByte			*currPtr = derOut;
253	DERSize			bytesLeft = *inOutLen;
254	DERSize			contentLen;
255	DERReturn		drtn;
256	DERSize			itemLen;
257	unsigned		dex;
258
259	/* top level tag */
260	itemLen = bytesLeft;
261    drtn = DEREncodeTag(topTag, currPtr, &itemLen);
262	if(drtn) {
263		return drtn;
264	}
265	currPtr += itemLen;
266	bytesLeft -= itemLen;
267	if(currPtr >= endPtr) {
268		return DR_BufOverflow;
269	}
270
271	/* content length */
272	contentLen = DERContentLengthOfEncodedSequence(src, numItems, itemSpecs);
273	itemLen = bytesLeft;
274	drtn = DEREncodeLength(contentLen, currPtr, &itemLen);
275	if(drtn) {
276		return drtn;
277	}
278	currPtr += itemLen;
279	bytesLeft -= itemLen;
280	if(currPtr + contentLen > endPtr) {
281		return DR_BufOverflow;
282	}
283	/* we don't have to check for overflow any more */
284
285	/* grind thru the items */
286	for(dex=0; dex<numItems; dex++) {
287		const DERItemSpec *currItemSpec = &itemSpecs[dex];
288		DERShort currOptions = currItemSpec->options;
289		const DERByte *byteSrc = (const DERByte *)src + currItemSpec->offset;
290		const DERItem *itemSrc = (const DERItem *)byteSrc;
291		int prependZero = 0;
292
293		if(currOptions & DER_ENC_WRITE_DER) {
294			/* easy case */
295			DERMemmove(currPtr, itemSrc->data, itemSrc->length);
296			currPtr += itemSrc->length;
297			bytesLeft -= itemSrc->length;
298			continue;
299		}
300
301        if ((currOptions & DER_DEC_OPTIONAL) && itemSrc->length == 0) {
302            /* If an optional item isn't present we skip it. */
303            continue;
304        }
305
306        /* encode one item: first the tag */
307        itemLen = bytesLeft;
308        drtn = DEREncodeTag(currItemSpec->tag, currPtr, &itemLen);
309        if(drtn) {
310            return drtn;
311        }
312        currPtr += itemLen;
313        bytesLeft -= itemLen;
314
315		/* do we need to prepend a zero to content? */
316		contentLen = itemSrc->length;
317		if((currOptions & DER_ENC_SIGNED_INT) &&
318		   (itemSrc->length != 0)) {
319			if(itemSrc->data[0] & 0x80) {
320				/* insert zero keep it positive */
321				contentLen++;
322				prependZero = 1;
323			}
324		}
325
326		/* encode content length */
327		itemLen = bytesLeft;
328		drtn = DEREncodeLength(contentLen, currPtr, &itemLen);
329		if(drtn) {
330			return drtn;
331		}
332		currPtr += itemLen;
333		bytesLeft -= itemLen;
334
335		/* now the content, with possible leading zero added */
336		if(prependZero) {
337			*currPtr++ = 0;
338			bytesLeft--;
339		}
340		DERMemmove(currPtr, itemSrc->data, itemSrc->length);
341		currPtr += itemSrc->length;
342		bytesLeft -= itemSrc->length;
343	}
344	*inOutLen = (currPtr - derOut);
345	return DR_Success;
346}
347
348/* calculate the length of an encoded sequence. */
349DERSize DERLengthOfEncodedSequence(
350    DERTag				topTag,
351	const void			*src,		/* generally a ptr to a struct full of
352									 *    DERItems */
353	DERShort			numItems,	/* size of itemSpecs[] */
354	const DERItemSpec	*itemSpecs)
355{
356	DERSize contentLen = DERContentLengthOfEncodedSequence(
357		src, numItems, itemSpecs);
358
359	return DERLengthOfTag(topTag) +
360		DERLengthOfLength(contentLen) +
361		contentLen;
362}
363
364#endif	/* DER_ENCODE_ENABLE */
365
366