1/* Copyright (c) 1998,2011,2014 Apple Inc.  All Rights Reserved.
2 *
3 * NOTICE: USE OF THE MATERIALS ACCOMPANYING THIS NOTICE IS SUBJECT
4 * TO THE TERMS OF THE SIGNED "FAST ELLIPTIC ENCRYPTION (FEE) REFERENCE
5 * SOURCE CODE EVALUATION AGREEMENT" BETWEEN APPLE, INC. AND THE
6 * ORIGINAL LICENSEE THAT OBTAINED THESE MATERIALS FROM APPLE,
7 * INC.  ANY USE OF THESE MATERIALS NOT PERMITTED BY SUCH AGREEMENT WILL
8 * EXPOSE YOU TO LIABILITY.
9 ***************************************************************************
10 *
11 * enc64.c - encode/decode in 64-char IA5 format, per RFC 1421
12 *
13 * Revision History
14 * ----------------
15 * 11/27/98	dmitch
16 *	Added ECDSA_VERIFY_ONLY dependencies.
17 * 10/06/98		ap
18 *	Changed to compile with C++.
19 * 12 Dec 96 at NeXT
20 *	Newlines optional in dec64() and isValidEnc64().
21 *  9 Oct 96 at NeXT
22 *	Created.
23 */
24
25#include "enc64.h"
26#include "falloc.h"
27
28/*
29 * 11/27/98 dmitch: The ECDSA_VERIFY_ONLY symbol, when #defined, disables all
30 * of the code in this module except that which is necessary for ECDSA
31 * siggnature verification.
32 */
33
34#ifndef	NULL
35#define NULL ((void *)0)
36#endif	/* NULL */
37
38/*
39 * map a 6-bit binary value to a printable character.
40 */
41static const
42unsigned char bintoasc[] =
43	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
44
45/*
46 * Map an 7-bit printable character to its corresponding binary value.
47 * Any illegal characters return high bit set.
48 */
49static const
50unsigned char asctobin[] =
51{
52    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
53    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
54    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
55    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
56    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
57    0x80, 0x80, 0x80, 0x3e, 0x80, 0x80, 0x80, 0x3f,
58    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
59    0x3c, 0x3d, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
60    0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
61    0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
62    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
63    0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80,
64    0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
65    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
66    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
67    0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80
68};
69
70/*
71 * map 6 bits to a printing char
72 */
73#define ENC(c) (bintoasc[((c) & 0x3f)])
74
75#define PAD		'='
76//#define ENC_LINE_LEN	64
77
78#ifndef	ECDSA_VERIFY_ONLY
79
80/*
81 * map one group of up to 3 bytes at inp to 4 bytes at outp.
82 * Count is number of valid bytes in *inp; if less than 3, the
83 * 1 or two extras must be zeros.
84 */
85static void encChunk(const unsigned char *inp,
86	unsigned char *outp,
87	int count)
88{
89	unsigned char c1, c2, c3, c4;
90
91	c1 = *inp >> 2;
92	c2 = ((inp[0] << 4) & 0x30) | ((inp[1] >> 4) & 0xf);
93	c3 = ((inp[1] << 2) & 0x3c) | ((inp[2] >> 6) & 0x3);
94	c4 = inp[2] & 0x3f;
95	*outp++ = ENC(c1);
96	*outp++ = ENC(c2);
97	if (count == 1) {
98	    *outp++ = PAD;
99	    *outp   = PAD;
100	} else {
101	    *outp++ = ENC(c3);
102	    if (count == 2) {
103		*outp = PAD;
104	    }
105	    else {
106		*outp = ENC(c4);
107	    }
108	}
109}
110
111/*
112 * Given input buffer inbuf, length inlen, encode to 64-char IA5 format.
113 * Result is fmalloc'd and returned; it is terminated by Microsoft-style
114 * newline and NULL. Its length (including the trailing newline and NULL)
115 * is returned in *outlen.
116 */
117
118unsigned char *enc64(const unsigned char *inbuf,
119	unsigned inlen,
120	unsigned *outlen)		// RETURNED
121{
122	return enc64WithLines(inbuf, inlen, 0, outlen);
123}
124
125unsigned char *enc64WithLines(const unsigned char *inbuf,
126	unsigned inlen,
127	unsigned linelen,
128	unsigned *outlen)
129{
130	unsigned	outTextLen;
131	unsigned 	len;			// to malloc, liberal
132	unsigned	olen = 0;		// actual output size
133	unsigned char 	*outbuf;
134	unsigned char 	endbuf[3];
135	unsigned		i;
136	unsigned char 	*outp;
137	unsigned	numLines;
138	unsigned	thisLine;
139
140	outTextLen = ((inlen + 2) / 3) * 4;
141	if(linelen) {
142	    /*
143	     * linelen must be 0 mod 4 for this to work; round up...
144	     */
145	    if((linelen & 0x03) != 0) {
146	        linelen = (linelen + 3) & 0xfffffffc;
147	    }
148	    numLines = (outTextLen + linelen - 1)/ linelen;
149	}
150	else {
151	    numLines = 1;
152	}
153
154	/*
155	 * Total output size = encoded text size plus one newline per
156	 * line of output, plus trailing NULL. For Microsoft compatibility,
157	 * we always generate newlines as \r\n; when decoding, we tolerate
158	 * \r\n or \n.
159	 */
160	len = outTextLen + (2 * numLines) + 1;
161	outbuf = (unsigned char*) fmalloc(len);
162	outp = outbuf;
163	thisLine = 0;
164
165	while(inlen) {
166	    if(inlen < 3) {
167		for(i=0; i<3; i++) {
168		    if(i < inlen) {
169			endbuf[i] = inbuf[i];
170		    }
171		    else {
172			endbuf[i] = 0;
173		    }
174		}
175		encChunk(endbuf, outp, inlen);
176		inlen = 0;
177	    }
178	    else {
179		encChunk(inbuf, outp, 3);
180		inlen -= 3;
181		inbuf += 3;
182	    }
183	    outp += 4;
184	    thisLine += 4;
185	    olen += 4;
186	    if((linelen != 0) && (thisLine >= linelen) && inlen) {
187	        /*
188		 * last trailing newline added below
189		 * Note we don't split 4-byte output chunks over newlines
190		 */
191	    	*outp++ = '\r';
192	    	*outp++ = '\n';
193		olen += 2;
194		thisLine = 0;
195	    }
196	}
197	*outp++ = '\r';
198	*outp++ = '\n';
199	*outp = '\0';
200	olen += 3;
201	*outlen = olen;
202	return outbuf;
203}
204
205#endif	/* ECDSA_VERIFY_ONLY */
206
207static inline int isWhite(unsigned char c)
208{
209	switch(c) {
210	    case '\n':
211
212	    case '\r':
213
214	    case ' ':
215
216	    case '\t':
217
218	    case '\0':
219		return 1;
220
221	    default:
222
223		return 0;
224
225	}
226}
227
228/*
229 * Strip off all whitespace from a (supposedly) enc64-format string.
230 * Returns a malloc'd string.
231 */
232static unsigned char *stringCleanse(const unsigned char *inbuf,
233	unsigned inlen,
234	unsigned *outlen)
235{
236	unsigned char	*news;			// cleansed inbuf
237	unsigned	newsDex;		// index into news
238	unsigned	i;
239
240	news = (unsigned char*) fmalloc(inlen);
241	newsDex = 0;
242	for(i=0; i<inlen; i++) {
243	    if(!isWhite(inbuf[i])) {
244	        news[newsDex++] = inbuf[i];
245	    }
246	}
247	*outlen = newsDex;
248	return news;
249}
250
251/*
252 * Given input buffer inbuf, length inlen, decode from 64-char IA5 format to
253 * binary. Result is fmalloced and returned; its length is returned in *outlen.
254 * NULL return indicates corrupted input.
255 *
256 * All whitespace in input is ignored.
257 */
258unsigned char *dec64(const unsigned char *inbuf,
259	unsigned inlen,
260	unsigned *outlen)
261{
262	unsigned char 		*outbuf;
263	unsigned char 		*outp;			// malloc'd outbuf size
264	unsigned 		obuflen;
265    	const unsigned char 	*bp;
266    	unsigned 		olen = 0;		// actual output size
267    	unsigned char 		c1, c2, c3, c4;
268    	unsigned char 		j;
269	unsigned		thisOlen;
270	unsigned char		*news;			// cleansed inbuf
271	unsigned		newsLen;
272
273	/*
274	 * Strip out all whitespace; remainder must be multiple of four
275	 * characters
276	 */
277	news = stringCleanse(inbuf, inlen, &newsLen);
278	if((newsLen & 0x03) != 0) {
279	    ffree(news);
280	    return (unsigned char*) NULL;
281	}
282	inlen = newsLen;
283	bp = news;
284
285	obuflen = (inlen / 4) * 3;
286	outbuf = (unsigned char*) fmalloc(obuflen);
287	outp = outbuf;
288
289	while (inlen) {
290	    /*
291	     * Note inlen is always a multiple of four here
292	     */
293	    if (*bp & 0x80 || (c1 = asctobin[*bp]) & 0x80) {
294	        goto errorOut;
295	    }
296	    inlen--;
297	    bp++;
298	    if (*bp & 0x80 || (c2 = asctobin[*bp]) & 0x80){
299	        goto errorOut;
300	    }
301	    inlen--;
302	    bp++;
303	    if (*bp == PAD) {
304	        /*
305		 * two input bytes, one output byte
306		 */
307		c3 = c4 = 0;
308		thisOlen = 1;
309		if (c2 & 0xf) {
310		    goto errorOut;
311		}
312		bp++;
313		inlen--;
314		if (*bp == PAD) {
315		    bp++;
316		    inlen--;
317		    if(inlen > 0) {
318			goto errorOut;
319		    }
320		}
321		else {
322		    goto errorOut;
323		}
324	    } else if (*bp & 0x80 || (c3 = asctobin[*bp]) & 0x80) {
325	    	goto errorOut;
326	    } else {
327	        bp++;
328		inlen--;
329		if (*bp == PAD) {
330		    /*
331		     * Three input bytes, two output
332		     */
333		    c4 = 0;
334		    thisOlen = 2;
335		    if (c3 & 3) {
336			goto errorOut;
337		    }
338		} else if (*bp & 0x80 || (c4 = asctobin[*bp]) & 0x80) {
339		    goto errorOut;
340		} else {
341		    /*
342		     * Normal non-pad case
343		     */
344		    thisOlen = 3;
345		}
346		bp++;
347		inlen--;
348	    }
349	    j = (c1 << 2) | (c2 >> 4);
350	    *outp++ = j;
351	    if(thisOlen > 1) {
352		j = (c2 << 4) | (c3 >> 2);
353		*outp++ = j;
354		if(thisOlen == 3) {
355		    j = (c3 << 6) | c4;
356		    *outp++ = j;
357		}
358	    }
359	    olen += thisOlen;
360	}
361        ffree(news);
362	*outlen = olen;
363	return outbuf;			/* normal return */
364
365errorOut:
366        ffree(news);
367	ffree(outbuf);
368	return (unsigned char*) NULL;
369}
370
371/*
372 * Determine if specified input data is valid enc64 format. Returns 1
373 * if valid, 0 if not.
374 * This doesn't do a full enc64 parse job; it scans for legal characters
375 * and proper sync when a possible pad is found.
376 */
377int isValidEnc64(const unsigned char *inbuf,
378	unsigned inlen)
379{
380	int padChars = 0;	// running count of PAD chars
381	int validEncChars = 0;
382	unsigned char c;
383
384	/*
385	 *   -- scan inbuf
386	 *   -- skip whitespace
387	 *   -- count valid chars
388	 *   -- ensure not more than 2 PAD chars, only at end
389	 *   -- ensure valid chars mod 4 == 0
390	 */
391
392	while(inlen) {
393	    c = *inbuf++;
394	    inlen--;
395	    if(isWhite(c)) {
396	        continue;
397	    }
398	    if(c == PAD) {
399		if(++padChars > 2) {
400		    return 0;		// max of 2 PAD chars at end
401		}
402	    }
403	    else if(padChars > 0) {
404		return 0;		// no normal chars after seeing PAD
405	    }
406	    else if((c & 0x80) || ((asctobin[c]) & 0x80)) {
407		return 0;		// invalid encoded char
408	    }
409	    validEncChars++;
410	}
411	if((validEncChars & 0x03) != 0) {
412	    return 0;
413	}
414	else {
415	    return 1;
416	}
417}
418