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