nsprpub/lib/libc/src/base64.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "plbase64.h"
michael@0 7 #include "prlog.h" /* For PR_NOT_REACHED */
michael@0 8 #include "prmem.h" /* for malloc / PR_MALLOC */
michael@0 9
michael@0 10 #include <string.h> /* for strlen */
michael@0 11
michael@0 12 static unsigned char *base = (unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
michael@0 13
michael@0 14 static void
michael@0 15 encode3to4
michael@0 16 (
michael@0 17 const unsigned char *src,
michael@0 18 unsigned char *dest
michael@0 19 )
michael@0 20 {
michael@0 21 PRUint32 b32 = (PRUint32)0;
michael@0 22 PRIntn i, j = 18;
michael@0 23
michael@0 24 for( i = 0; i < 3; i++ )
michael@0 25 {
michael@0 26 b32 <<= 8;
michael@0 27 b32 |= (PRUint32)src[i];
michael@0 28 }
michael@0 29
michael@0 30 for( i = 0; i < 4; i++ )
michael@0 31 {
michael@0 32 dest[i] = base[ (PRUint32)((b32>>j) & 0x3F) ];
michael@0 33 j -= 6;
michael@0 34 }
michael@0 35
michael@0 36 return;
michael@0 37 }
michael@0 38
michael@0 39 static void
michael@0 40 encode2to4
michael@0 41 (
michael@0 42 const unsigned char *src,
michael@0 43 unsigned char *dest
michael@0 44 )
michael@0 45 {
michael@0 46 dest[0] = base[ (PRUint32)((src[0]>>2) & 0x3F) ];
michael@0 47 dest[1] = base[ (PRUint32)(((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0F)) ];
michael@0 48 dest[2] = base[ (PRUint32)((src[1] & 0x0F) << 2) ];
michael@0 49 dest[3] = (unsigned char)'=';
michael@0 50 return;
michael@0 51 }
michael@0 52
michael@0 53 static void
michael@0 54 encode1to4
michael@0 55 (
michael@0 56 const unsigned char *src,
michael@0 57 unsigned char *dest
michael@0 58 )
michael@0 59 {
michael@0 60 dest[0] = base[ (PRUint32)((src[0]>>2) & 0x3F) ];
michael@0 61 dest[1] = base[ (PRUint32)((src[0] & 0x03) << 4) ];
michael@0 62 dest[2] = (unsigned char)'=';
michael@0 63 dest[3] = (unsigned char)'=';
michael@0 64 return;
michael@0 65 }
michael@0 66
michael@0 67 static void
michael@0 68 encode
michael@0 69 (
michael@0 70 const unsigned char *src,
michael@0 71 PRUint32 srclen,
michael@0 72 unsigned char *dest
michael@0 73 )
michael@0 74 {
michael@0 75 while( srclen >= 3 )
michael@0 76 {
michael@0 77 encode3to4(src, dest);
michael@0 78 src += 3;
michael@0 79 dest += 4;
michael@0 80 srclen -= 3;
michael@0 81 }
michael@0 82
michael@0 83 switch( srclen )
michael@0 84 {
michael@0 85 case 2:
michael@0 86 encode2to4(src, dest);
michael@0 87 break;
michael@0 88 case 1:
michael@0 89 encode1to4(src, dest);
michael@0 90 break;
michael@0 91 case 0:
michael@0 92 break;
michael@0 93 default:
michael@0 94 PR_NOT_REACHED("coding error");
michael@0 95 }
michael@0 96
michael@0 97 return;
michael@0 98 }
michael@0 99
michael@0 100 /*
michael@0 101 * PL_Base64Encode
michael@0 102 *
michael@0 103 * If the destination argument is NULL, a return buffer is
michael@0 104 * allocated, and the data therein will be null-terminated.
michael@0 105 * If the destination argument is not NULL, it is assumed to
michael@0 106 * be of sufficient size, and the contents will not be null-
michael@0 107 * terminated by this routine.
michael@0 108 *
michael@0 109 * Returns null if the allocation fails.
michael@0 110 */
michael@0 111
michael@0 112 PR_IMPLEMENT(char *)
michael@0 113 PL_Base64Encode
michael@0 114 (
michael@0 115 const char *src,
michael@0 116 PRUint32 srclen,
michael@0 117 char *dest
michael@0 118 )
michael@0 119 {
michael@0 120 if( 0 == srclen )
michael@0 121 {
michael@0 122 size_t len = strlen(src);
michael@0 123 srclen = len;
michael@0 124 /* Detect truncation. */
michael@0 125 if( srclen != len )
michael@0 126 {
michael@0 127 return (char *)0;
michael@0 128 }
michael@0 129 }
michael@0 130
michael@0 131 if( (char *)0 == dest )
michael@0 132 {
michael@0 133 PRUint32 destlen;
michael@0 134 /* Ensure all PRUint32 values stay within range. */
michael@0 135 if( srclen > (PR_UINT32_MAX/4) * 3 )
michael@0 136 {
michael@0 137 return (char *)0;
michael@0 138 }
michael@0 139 destlen = ((srclen + 2)/3) * 4;
michael@0 140 dest = (char *)PR_MALLOC(destlen + 1);
michael@0 141 if( (char *)0 == dest )
michael@0 142 {
michael@0 143 return (char *)0;
michael@0 144 }
michael@0 145 dest[ destlen ] = (char)0; /* null terminate */
michael@0 146 }
michael@0 147
michael@0 148 encode((const unsigned char *)src, srclen, (unsigned char *)dest);
michael@0 149 return dest;
michael@0 150 }
michael@0 151
michael@0 152 static PRInt32
michael@0 153 codetovalue
michael@0 154 (
michael@0 155 unsigned char c
michael@0 156 )
michael@0 157 {
michael@0 158 if( (c >= (unsigned char)'A') && (c <= (unsigned char)'Z') )
michael@0 159 {
michael@0 160 return (PRInt32)(c - (unsigned char)'A');
michael@0 161 }
michael@0 162 else if( (c >= (unsigned char)'a') && (c <= (unsigned char)'z') )
michael@0 163 {
michael@0 164 return ((PRInt32)(c - (unsigned char)'a') +26);
michael@0 165 }
michael@0 166 else if( (c >= (unsigned char)'0') && (c <= (unsigned char)'9') )
michael@0 167 {
michael@0 168 return ((PRInt32)(c - (unsigned char)'0') +52);
michael@0 169 }
michael@0 170 else if( (unsigned char)'+' == c )
michael@0 171 {
michael@0 172 return (PRInt32)62;
michael@0 173 }
michael@0 174 else if( (unsigned char)'/' == c )
michael@0 175 {
michael@0 176 return (PRInt32)63;
michael@0 177 }
michael@0 178 else
michael@0 179 {
michael@0 180 return -1;
michael@0 181 }
michael@0 182 }
michael@0 183
michael@0 184 static PRStatus
michael@0 185 decode4to3
michael@0 186 (
michael@0 187 const unsigned char *src,
michael@0 188 unsigned char *dest
michael@0 189 )
michael@0 190 {
michael@0 191 PRUint32 b32 = (PRUint32)0;
michael@0 192 PRInt32 bits;
michael@0 193 PRIntn i;
michael@0 194
michael@0 195 for( i = 0; i < 4; i++ )
michael@0 196 {
michael@0 197 bits = codetovalue(src[i]);
michael@0 198 if( bits < 0 )
michael@0 199 {
michael@0 200 return PR_FAILURE;
michael@0 201 }
michael@0 202
michael@0 203 b32 <<= 6;
michael@0 204 b32 |= bits;
michael@0 205 }
michael@0 206
michael@0 207 dest[0] = (unsigned char)((b32 >> 16) & 0xFF);
michael@0 208 dest[1] = (unsigned char)((b32 >> 8) & 0xFF);
michael@0 209 dest[2] = (unsigned char)((b32 ) & 0xFF);
michael@0 210
michael@0 211 return PR_SUCCESS;
michael@0 212 }
michael@0 213
michael@0 214 static PRStatus
michael@0 215 decode3to2
michael@0 216 (
michael@0 217 const unsigned char *src,
michael@0 218 unsigned char *dest
michael@0 219 )
michael@0 220 {
michael@0 221 PRUint32 b32 = (PRUint32)0;
michael@0 222 PRInt32 bits;
michael@0 223 PRUint32 ubits;
michael@0 224
michael@0 225 bits = codetovalue(src[0]);
michael@0 226 if( bits < 0 )
michael@0 227 {
michael@0 228 return PR_FAILURE;
michael@0 229 }
michael@0 230
michael@0 231 b32 = (PRUint32)bits;
michael@0 232 b32 <<= 6;
michael@0 233
michael@0 234 bits = codetovalue(src[1]);
michael@0 235 if( bits < 0 )
michael@0 236 {
michael@0 237 return PR_FAILURE;
michael@0 238 }
michael@0 239
michael@0 240 b32 |= (PRUint32)bits;
michael@0 241 b32 <<= 4;
michael@0 242
michael@0 243 bits = codetovalue(src[2]);
michael@0 244 if( bits < 0 )
michael@0 245 {
michael@0 246 return PR_FAILURE;
michael@0 247 }
michael@0 248
michael@0 249 ubits = (PRUint32)bits;
michael@0 250 b32 |= (ubits >> 2);
michael@0 251
michael@0 252 dest[0] = (unsigned char)((b32 >> 8) & 0xFF);
michael@0 253 dest[1] = (unsigned char)((b32 ) & 0xFF);
michael@0 254
michael@0 255 return PR_SUCCESS;
michael@0 256 }
michael@0 257
michael@0 258 static PRStatus
michael@0 259 decode2to1
michael@0 260 (
michael@0 261 const unsigned char *src,
michael@0 262 unsigned char *dest
michael@0 263 )
michael@0 264 {
michael@0 265 PRUint32 b32;
michael@0 266 PRUint32 ubits;
michael@0 267 PRInt32 bits;
michael@0 268
michael@0 269 bits = codetovalue(src[0]);
michael@0 270 if( bits < 0 )
michael@0 271 {
michael@0 272 return PR_FAILURE;
michael@0 273 }
michael@0 274
michael@0 275 ubits = (PRUint32)bits;
michael@0 276 b32 = (ubits << 2);
michael@0 277
michael@0 278 bits = codetovalue(src[1]);
michael@0 279 if( bits < 0 )
michael@0 280 {
michael@0 281 return PR_FAILURE;
michael@0 282 }
michael@0 283
michael@0 284 ubits = (PRUint32)bits;
michael@0 285 b32 |= (ubits >> 4);
michael@0 286
michael@0 287 dest[0] = (unsigned char)b32;
michael@0 288
michael@0 289 return PR_SUCCESS;
michael@0 290 }
michael@0 291
michael@0 292 static PRStatus
michael@0 293 decode
michael@0 294 (
michael@0 295 const unsigned char *src,
michael@0 296 PRUint32 srclen,
michael@0 297 unsigned char *dest
michael@0 298 )
michael@0 299 {
michael@0 300 PRStatus rv;
michael@0 301
michael@0 302 while( srclen >= 4 )
michael@0 303 {
michael@0 304 rv = decode4to3(src, dest);
michael@0 305 if( PR_SUCCESS != rv )
michael@0 306 {
michael@0 307 return PR_FAILURE;
michael@0 308 }
michael@0 309
michael@0 310 src += 4;
michael@0 311 dest += 3;
michael@0 312 srclen -= 4;
michael@0 313 }
michael@0 314
michael@0 315 switch( srclen )
michael@0 316 {
michael@0 317 case 3:
michael@0 318 rv = decode3to2(src, dest);
michael@0 319 break;
michael@0 320 case 2:
michael@0 321 rv = decode2to1(src, dest);
michael@0 322 break;
michael@0 323 case 1:
michael@0 324 rv = PR_FAILURE;
michael@0 325 break;
michael@0 326 case 0:
michael@0 327 rv = PR_SUCCESS;
michael@0 328 break;
michael@0 329 default:
michael@0 330 PR_NOT_REACHED("coding error");
michael@0 331 }
michael@0 332
michael@0 333 return rv;
michael@0 334 }
michael@0 335
michael@0 336 /*
michael@0 337 * PL_Base64Decode
michael@0 338 *
michael@0 339 * If the destination argument is NULL, a return buffer is
michael@0 340 * allocated and the data therein will be null-terminated.
michael@0 341 * If the destination argument is not null, it is assumed
michael@0 342 * to be of sufficient size, and the data will not be null-
michael@0 343 * terminated by this routine.
michael@0 344 *
michael@0 345 * Returns null if the allocation fails, or if the source string is
michael@0 346 * not well-formed.
michael@0 347 */
michael@0 348
michael@0 349 PR_IMPLEMENT(char *)
michael@0 350 PL_Base64Decode
michael@0 351 (
michael@0 352 const char *src,
michael@0 353 PRUint32 srclen,
michael@0 354 char *dest
michael@0 355 )
michael@0 356 {
michael@0 357 PRStatus status;
michael@0 358 PRBool allocated = PR_FALSE;
michael@0 359
michael@0 360 if( (char *)0 == src )
michael@0 361 {
michael@0 362 return (char *)0;
michael@0 363 }
michael@0 364
michael@0 365 if( 0 == srclen )
michael@0 366 {
michael@0 367 size_t len = strlen(src);
michael@0 368 srclen = len;
michael@0 369 /* Detect truncation. */
michael@0 370 if( srclen != len )
michael@0 371 {
michael@0 372 return (char *)0;
michael@0 373 }
michael@0 374 }
michael@0 375
michael@0 376 if( srclen && (0 == (srclen & 3)) )
michael@0 377 {
michael@0 378 if( (char)'=' == src[ srclen-1 ] )
michael@0 379 {
michael@0 380 if( (char)'=' == src[ srclen-2 ] )
michael@0 381 {
michael@0 382 srclen -= 2;
michael@0 383 }
michael@0 384 else
michael@0 385 {
michael@0 386 srclen -= 1;
michael@0 387 }
michael@0 388 }
michael@0 389 }
michael@0 390
michael@0 391 if( (char *)0 == dest )
michael@0 392 {
michael@0 393 /* The following computes ((srclen * 3) / 4) without overflow. */
michael@0 394 PRUint32 destlen = (srclen / 4) * 3 + ((srclen % 4) * 3) / 4;
michael@0 395 dest = (char *)PR_MALLOC(destlen + 1);
michael@0 396 if( (char *)0 == dest )
michael@0 397 {
michael@0 398 return (char *)0;
michael@0 399 }
michael@0 400 dest[ destlen ] = (char)0; /* null terminate */
michael@0 401 allocated = PR_TRUE;
michael@0 402 }
michael@0 403
michael@0 404 status = decode((const unsigned char *)src, srclen, (unsigned char *)dest);
michael@0 405 if( PR_SUCCESS != status )
michael@0 406 {
michael@0 407 if( PR_TRUE == allocated )
michael@0 408 {
michael@0 409 PR_DELETE(dest);
michael@0 410 }
michael@0 411
michael@0 412 return (char *)0;
michael@0 413 }
michael@0 414
michael@0 415 return dest;
michael@0 416 }

mercurial