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