security/nss/lib/util/nssb64e.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /*
michael@0 6 * Base64 encoding (binary to ascii).
michael@0 7 */
michael@0 8
michael@0 9 #include "nssb64.h"
michael@0 10 #include "nspr.h"
michael@0 11 #include "secitem.h"
michael@0 12 #include "secerr.h"
michael@0 13
michael@0 14 /*
michael@0 15 * XXX See the big comment at the top of nssb64d.c about moving the
michael@0 16 * bulk of this code over into NSPR (the PL part). It all applies
michael@0 17 * here but I didn't want to duplicate it, to avoid divergence problems.
michael@0 18 */
michael@0 19
michael@0 20 /*
michael@0 21 **************************************************************
michael@0 22 * XXX Beginning of base64 encoding code to be moved into NSPR.
michael@0 23 */
michael@0 24
michael@0 25
michael@0 26 struct PLBase64EncodeStateStr {
michael@0 27 unsigned chunks;
michael@0 28 unsigned saved;
michael@0 29 unsigned char buf[3];
michael@0 30 };
michael@0 31
michael@0 32 /*
michael@0 33 * This typedef would belong in the NSPR header file (i.e. plbase64.h).
michael@0 34 */
michael@0 35 typedef struct PLBase64EncoderStr PLBase64Encoder;
michael@0 36
michael@0 37 /*
michael@0 38 * The following implementation of base64 encoding was based on code
michael@0 39 * found in libmime (specifically, in mimeenc.c). It has been adapted to
michael@0 40 * use PR types and naming as well as to provide other necessary semantics
michael@0 41 * (like buffer-in/buffer-out in addition to "streaming" without undue
michael@0 42 * performance hit of extra copying if you made the buffer versions
michael@0 43 * use the output_fn). It also incorporates some aspects of the current
michael@0 44 * NSPR base64 encoding code. As such, you may find similarities to
michael@0 45 * both of those implementations. I tried to use names that reflected
michael@0 46 * the original code when possible. For this reason you may find some
michael@0 47 * inconsistencies -- libmime used lots of "in" and "out" whereas the
michael@0 48 * NSPR version uses "src" and "dest"; sometimes I changed one to the other
michael@0 49 * and sometimes I left them when I thought the subroutines were at least
michael@0 50 * self-consistent.
michael@0 51 */
michael@0 52
michael@0 53 PR_BEGIN_EXTERN_C
michael@0 54
michael@0 55 /*
michael@0 56 * Opaque object used by the encoder to store state.
michael@0 57 */
michael@0 58 struct PLBase64EncoderStr {
michael@0 59 /*
michael@0 60 * The one or two bytes pending. (We need 3 to create a "token",
michael@0 61 * and hold the leftovers here. in_buffer_count is *only* ever
michael@0 62 * 0, 1, or 2.
michael@0 63 */
michael@0 64 unsigned char in_buffer[2];
michael@0 65 int in_buffer_count;
michael@0 66
michael@0 67 /*
michael@0 68 * If the caller wants linebreaks added, line_length specifies
michael@0 69 * where they come out. It must be a multiple of 4; if the caller
michael@0 70 * provides one that isn't, we round it down to the nearest
michael@0 71 * multiple of 4.
michael@0 72 *
michael@0 73 * The value of current_column counts how many characters have been
michael@0 74 * added since the last linebreaks (or since the beginning, on the
michael@0 75 * first line). It is also always a multiple of 4; it is unused when
michael@0 76 * line_length is 0.
michael@0 77 */
michael@0 78 PRUint32 line_length;
michael@0 79 PRUint32 current_column;
michael@0 80
michael@0 81 /*
michael@0 82 * Where to write the encoded data (used when streaming, not when
michael@0 83 * doing all in-memory (buffer) operations).
michael@0 84 *
michael@0 85 * Note that this definition is chosen to be compatible with PR_Write.
michael@0 86 */
michael@0 87 PRInt32 (*output_fn) (void *output_arg, const char *buf, PRInt32 size);
michael@0 88 void *output_arg;
michael@0 89
michael@0 90 /*
michael@0 91 * Where the encoded output goes -- either temporarily (in the streaming
michael@0 92 * case, staged here before it goes to the output function) or what will
michael@0 93 * be the entire buffered result for users of the buffer version.
michael@0 94 */
michael@0 95 char *output_buffer;
michael@0 96 PRUint32 output_buflen; /* the total length of allocated buffer */
michael@0 97 PRUint32 output_length; /* the length that is currently populated */
michael@0 98 };
michael@0 99
michael@0 100 PR_END_EXTERN_C
michael@0 101
michael@0 102
michael@0 103 /*
michael@0 104 * Table to convert a binary value to its corresponding ascii "code".
michael@0 105 */
michael@0 106 static unsigned char base64_valuetocode[64] =
michael@0 107 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
michael@0 108
michael@0 109 #define B64_PAD '='
michael@0 110 #define B64_CR '\r'
michael@0 111 #define B64_LF '\n'
michael@0 112
michael@0 113 static PRStatus
michael@0 114 pl_base64_encode_buffer (PLBase64Encoder *data, const unsigned char *in,
michael@0 115 PRUint32 size)
michael@0 116 {
michael@0 117 const unsigned char *end = in + size;
michael@0 118 char *out = data->output_buffer + data->output_length;
michael@0 119 unsigned int i = data->in_buffer_count;
michael@0 120 PRUint32 n = 0;
michael@0 121 int off;
michael@0 122 PRUint32 output_threshold;
michael@0 123
michael@0 124 /* If this input buffer is too small, wait until next time. */
michael@0 125 if (size < (3 - i)) {
michael@0 126 data->in_buffer[i++] = in[0];
michael@0 127 if (size > 1)
michael@0 128 data->in_buffer[i++] = in[1];
michael@0 129 PR_ASSERT(i < 3);
michael@0 130 data->in_buffer_count = i;
michael@0 131 return PR_SUCCESS;
michael@0 132 }
michael@0 133
michael@0 134 /* If there are bytes that were put back last time, take them now. */
michael@0 135 if (i > 0) {
michael@0 136 n = data->in_buffer[0];
michael@0 137 if (i > 1)
michael@0 138 n = (n << 8) | data->in_buffer[1];
michael@0 139 data->in_buffer_count = 0;
michael@0 140 }
michael@0 141
michael@0 142 /* If our total is not a multiple of three, put one or two bytes back. */
michael@0 143 off = (size + i) % 3;
michael@0 144 if (off > 0) {
michael@0 145 size -= off;
michael@0 146 data->in_buffer[0] = in[size];
michael@0 147 if (off > 1)
michael@0 148 data->in_buffer[1] = in[size + 1];
michael@0 149 data->in_buffer_count = off;
michael@0 150 end -= off;
michael@0 151 }
michael@0 152
michael@0 153 output_threshold = data->output_buflen - 3;
michael@0 154
michael@0 155 /*
michael@0 156 * Populate the output buffer with base64 data, one line (or buffer)
michael@0 157 * at a time.
michael@0 158 */
michael@0 159 while (in < end) {
michael@0 160 int j, k;
michael@0 161
michael@0 162 while (i < 3) {
michael@0 163 n = (n << 8) | *in++;
michael@0 164 i++;
michael@0 165 }
michael@0 166 i = 0;
michael@0 167
michael@0 168 if (data->line_length > 0) {
michael@0 169 if (data->current_column >= data->line_length) {
michael@0 170 data->current_column = 0;
michael@0 171 *out++ = B64_CR;
michael@0 172 *out++ = B64_LF;
michael@0 173 data->output_length += 2;
michael@0 174 }
michael@0 175 data->current_column += 4; /* the bytes we are about to add */
michael@0 176 }
michael@0 177
michael@0 178 for (j = 18; j >= 0; j -= 6) {
michael@0 179 k = (n >> j) & 0x3F;
michael@0 180 *out++ = base64_valuetocode[k];
michael@0 181 }
michael@0 182 n = 0;
michael@0 183 data->output_length += 4;
michael@0 184
michael@0 185 if (data->output_length >= output_threshold) {
michael@0 186 PR_ASSERT(data->output_length <= data->output_buflen);
michael@0 187 if (data->output_fn != NULL) {
michael@0 188 PRInt32 output_result;
michael@0 189
michael@0 190 output_result = data->output_fn (data->output_arg,
michael@0 191 data->output_buffer,
michael@0 192 (PRInt32) data->output_length);
michael@0 193 if (output_result < 0)
michael@0 194 return PR_FAILURE;
michael@0 195
michael@0 196 out = data->output_buffer;
michael@0 197 data->output_length = 0;
michael@0 198 } else {
michael@0 199 /*
michael@0 200 * Check that we are about to exit the loop. (Since we
michael@0 201 * are over the threshold, there isn't enough room in the
michael@0 202 * output buffer for another trip around.)
michael@0 203 */
michael@0 204 PR_ASSERT(in == end);
michael@0 205 if (in < end) {
michael@0 206 PR_SetError (PR_BUFFER_OVERFLOW_ERROR, 0);
michael@0 207 return PR_FAILURE;
michael@0 208 }
michael@0 209 }
michael@0 210 }
michael@0 211 }
michael@0 212
michael@0 213 return PR_SUCCESS;
michael@0 214 }
michael@0 215
michael@0 216 static PRStatus
michael@0 217 pl_base64_encode_flush (PLBase64Encoder *data)
michael@0 218 {
michael@0 219 int i = data->in_buffer_count;
michael@0 220
michael@0 221 if (i == 0 && data->output_length == 0)
michael@0 222 return PR_SUCCESS;
michael@0 223
michael@0 224 if (i > 0) {
michael@0 225 char *out = data->output_buffer + data->output_length;
michael@0 226 PRUint32 n;
michael@0 227 int j, k;
michael@0 228
michael@0 229 n = ((PRUint32) data->in_buffer[0]) << 16;
michael@0 230 if (i > 1)
michael@0 231 n |= ((PRUint32) data->in_buffer[1] << 8);
michael@0 232
michael@0 233 data->in_buffer_count = 0;
michael@0 234
michael@0 235 if (data->line_length > 0) {
michael@0 236 if (data->current_column >= data->line_length) {
michael@0 237 data->current_column = 0;
michael@0 238 *out++ = B64_CR;
michael@0 239 *out++ = B64_LF;
michael@0 240 data->output_length += 2;
michael@0 241 }
michael@0 242 }
michael@0 243
michael@0 244 /*
michael@0 245 * This will fill in more than we really have data for, but the
michael@0 246 * valid parts will end up in the correct position and the extras
michael@0 247 * will be over-written with pad characters below.
michael@0 248 */
michael@0 249 for (j = 18; j >= 0; j -= 6) {
michael@0 250 k = (n >> j) & 0x3F;
michael@0 251 *out++ = base64_valuetocode[k];
michael@0 252 }
michael@0 253
michael@0 254 /* Pad with equal-signs. */
michael@0 255 if (i == 1)
michael@0 256 out[-2] = B64_PAD;
michael@0 257 out[-1] = B64_PAD;
michael@0 258
michael@0 259 data->output_length += 4;
michael@0 260 }
michael@0 261
michael@0 262 if (data->output_fn != NULL) {
michael@0 263 PRInt32 output_result;
michael@0 264
michael@0 265 output_result = data->output_fn (data->output_arg, data->output_buffer,
michael@0 266 (PRInt32) data->output_length);
michael@0 267 data->output_length = 0;
michael@0 268
michael@0 269 if (output_result < 0)
michael@0 270 return PR_FAILURE;
michael@0 271 }
michael@0 272
michael@0 273 return PR_SUCCESS;
michael@0 274 }
michael@0 275
michael@0 276
michael@0 277 /*
michael@0 278 * The maximum space needed to hold the output of the encoder given input
michael@0 279 * data of length "size", and allowing for CRLF added at least every
michael@0 280 * line_length bytes (we will add it at nearest lower multiple of 4).
michael@0 281 * There is no trailing CRLF.
michael@0 282 */
michael@0 283 static PRUint32
michael@0 284 PL_Base64MaxEncodedLength (PRUint32 size, PRUint32 line_length)
michael@0 285 {
michael@0 286 PRUint32 tokens, tokens_per_line, full_lines, line_break_chars, remainder;
michael@0 287
michael@0 288 tokens = (size + 2) / 3;
michael@0 289
michael@0 290 if (line_length == 0)
michael@0 291 return tokens * 4;
michael@0 292
michael@0 293 if (line_length < 4) /* too small! */
michael@0 294 line_length = 4;
michael@0 295
michael@0 296 tokens_per_line = line_length / 4;
michael@0 297 full_lines = tokens / tokens_per_line;
michael@0 298 remainder = (tokens - (full_lines * tokens_per_line)) * 4;
michael@0 299 line_break_chars = full_lines * 2;
michael@0 300 if (remainder == 0)
michael@0 301 line_break_chars -= 2;
michael@0 302
michael@0 303 return (full_lines * tokens_per_line * 4) + line_break_chars + remainder;
michael@0 304 }
michael@0 305
michael@0 306
michael@0 307 /*
michael@0 308 * A distinct internal creation function for the buffer version to use.
michael@0 309 * (It does not want to specify an output_fn, and we want the normal
michael@0 310 * Create function to require that.) All common initialization of the
michael@0 311 * encoding context should be done *here*.
michael@0 312 *
michael@0 313 * Save "line_length", rounded down to nearest multiple of 4 (if not
michael@0 314 * already even multiple). Allocate output_buffer, if not provided --
michael@0 315 * based on given size if specified, otherwise based on line_length.
michael@0 316 */
michael@0 317 static PLBase64Encoder *
michael@0 318 pl_base64_create_encoder (PRUint32 line_length, char *output_buffer,
michael@0 319 PRUint32 output_buflen)
michael@0 320 {
michael@0 321 PLBase64Encoder *data;
michael@0 322 PRUint32 line_tokens;
michael@0 323
michael@0 324 data = PR_NEWZAP(PLBase64Encoder);
michael@0 325 if (data == NULL)
michael@0 326 return NULL;
michael@0 327
michael@0 328 if (line_length > 0 && line_length < 4) /* too small! */
michael@0 329 line_length = 4;
michael@0 330
michael@0 331 line_tokens = line_length / 4;
michael@0 332 data->line_length = line_tokens * 4;
michael@0 333
michael@0 334 if (output_buffer == NULL) {
michael@0 335 if (output_buflen == 0) {
michael@0 336 if (data->line_length > 0) /* need to include room for CRLF */
michael@0 337 output_buflen = data->line_length + 2;
michael@0 338 else
michael@0 339 output_buflen = 64; /* XXX what is a good size? */
michael@0 340 }
michael@0 341
michael@0 342 output_buffer = (char *) PR_Malloc(output_buflen);
michael@0 343 if (output_buffer == NULL) {
michael@0 344 PR_Free(data);
michael@0 345 return NULL;
michael@0 346 }
michael@0 347 }
michael@0 348
michael@0 349 data->output_buffer = output_buffer;
michael@0 350 data->output_buflen = output_buflen;
michael@0 351 return data;
michael@0 352 }
michael@0 353
michael@0 354 /*
michael@0 355 * Function to start a base64 encoding context.
michael@0 356 * An "output_fn" is required; the "output_arg" parameter to that is optional.
michael@0 357 * If linebreaks in the encoded output are desired, "line_length" specifies
michael@0 358 * where to place them -- it will be rounded down to the nearest multiple of 4
michael@0 359 * (if it is not already an even multiple of 4). If it is zero, no linebreaks
michael@0 360 * will be added. (FYI, a linebreak is CRLF -- two characters.)
michael@0 361 */
michael@0 362 static PLBase64Encoder *
michael@0 363 PL_CreateBase64Encoder (PRInt32 (*output_fn) (void *, const char *, PRInt32),
michael@0 364 void *output_arg, PRUint32 line_length)
michael@0 365 {
michael@0 366 PLBase64Encoder *data;
michael@0 367
michael@0 368 if (output_fn == NULL) {
michael@0 369 PR_SetError (PR_INVALID_ARGUMENT_ERROR, 0);
michael@0 370 return NULL;
michael@0 371 }
michael@0 372
michael@0 373 data = pl_base64_create_encoder (line_length, NULL, 0);
michael@0 374 if (data == NULL)
michael@0 375 return NULL;
michael@0 376
michael@0 377 data->output_fn = output_fn;
michael@0 378 data->output_arg = output_arg;
michael@0 379
michael@0 380 return data;
michael@0 381 }
michael@0 382
michael@0 383
michael@0 384 /*
michael@0 385 * Push data through the encoder, causing the output_fn (provided to Create)
michael@0 386 * to be called with the encoded data.
michael@0 387 */
michael@0 388 static PRStatus
michael@0 389 PL_UpdateBase64Encoder (PLBase64Encoder *data, const unsigned char *buffer,
michael@0 390 PRUint32 size)
michael@0 391 {
michael@0 392 /* XXX Should we do argument checking only in debug build? */
michael@0 393 if (data == NULL || buffer == NULL || size == 0) {
michael@0 394 PR_SetError (PR_INVALID_ARGUMENT_ERROR, 0);
michael@0 395 return PR_FAILURE;
michael@0 396 }
michael@0 397
michael@0 398 return pl_base64_encode_buffer (data, buffer, size);
michael@0 399 }
michael@0 400
michael@0 401
michael@0 402 /*
michael@0 403 * When you're done encoding, call this to free the data. If "abort_p"
michael@0 404 * is false, then calling this may cause the output_fn to be called
michael@0 405 * one last time (as the last buffered data is flushed out).
michael@0 406 */
michael@0 407 static PRStatus
michael@0 408 PL_DestroyBase64Encoder (PLBase64Encoder *data, PRBool abort_p)
michael@0 409 {
michael@0 410 PRStatus status = PR_SUCCESS;
michael@0 411
michael@0 412 /* XXX Should we do argument checking only in debug build? */
michael@0 413 if (data == NULL) {
michael@0 414 PR_SetError (PR_INVALID_ARGUMENT_ERROR, 0);
michael@0 415 return PR_FAILURE;
michael@0 416 }
michael@0 417
michael@0 418 /* Flush out the last few buffered characters. */
michael@0 419 if (!abort_p)
michael@0 420 status = pl_base64_encode_flush (data);
michael@0 421
michael@0 422 if (data->output_buffer != NULL)
michael@0 423 PR_Free(data->output_buffer);
michael@0 424 PR_Free(data);
michael@0 425
michael@0 426 return status;
michael@0 427 }
michael@0 428
michael@0 429
michael@0 430 /*
michael@0 431 * Perform base64 encoding from an input buffer to an output buffer.
michael@0 432 * The output buffer can be provided (as "dest"); you can also pass in
michael@0 433 * a NULL and this function will allocate a buffer large enough for you,
michael@0 434 * and return it. If you do provide the output buffer, you must also
michael@0 435 * provide the maximum length of that buffer (as "maxdestlen").
michael@0 436 * The actual encoded length of output will be returned to you in
michael@0 437 * "output_destlen".
michael@0 438 *
michael@0 439 * If linebreaks in the encoded output are desired, "line_length" specifies
michael@0 440 * where to place them -- it will be rounded down to the nearest multiple of 4
michael@0 441 * (if it is not already an even multiple of 4). If it is zero, no linebreaks
michael@0 442 * will be added. (FYI, a linebreak is CRLF -- two characters.)
michael@0 443 *
michael@0 444 * Return value is NULL on error, the output buffer (allocated or provided)
michael@0 445 * otherwise.
michael@0 446 */
michael@0 447 static char *
michael@0 448 PL_Base64EncodeBuffer (const unsigned char *src, PRUint32 srclen,
michael@0 449 PRUint32 line_length, char *dest, PRUint32 maxdestlen,
michael@0 450 PRUint32 *output_destlen)
michael@0 451 {
michael@0 452 PRUint32 need_length;
michael@0 453 PLBase64Encoder *data = NULL;
michael@0 454 PRStatus status;
michael@0 455
michael@0 456 PR_ASSERT(srclen > 0);
michael@0 457 if (srclen == 0)
michael@0 458 return dest;
michael@0 459
michael@0 460 /*
michael@0 461 * How much space could we possibly need for encoding this input?
michael@0 462 */
michael@0 463 need_length = PL_Base64MaxEncodedLength (srclen, line_length);
michael@0 464
michael@0 465 /*
michael@0 466 * Make sure we have at least that much, if output buffer provided.
michael@0 467 */
michael@0 468 if (dest != NULL) {
michael@0 469 PR_ASSERT(maxdestlen >= need_length);
michael@0 470 if (maxdestlen < need_length) {
michael@0 471 PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
michael@0 472 return NULL;
michael@0 473 }
michael@0 474 } else {
michael@0 475 maxdestlen = need_length;
michael@0 476 }
michael@0 477
michael@0 478 data = pl_base64_create_encoder(line_length, dest, maxdestlen);
michael@0 479 if (data == NULL)
michael@0 480 return NULL;
michael@0 481
michael@0 482 status = pl_base64_encode_buffer (data, src, srclen);
michael@0 483
michael@0 484 /*
michael@0 485 * We do not wait for Destroy to flush, because Destroy will also
michael@0 486 * get rid of our encoder context, which we need to look at first!
michael@0 487 */
michael@0 488 if (status == PR_SUCCESS)
michael@0 489 status = pl_base64_encode_flush (data);
michael@0 490
michael@0 491 if (status != PR_SUCCESS) {
michael@0 492 (void) PL_DestroyBase64Encoder (data, PR_TRUE);
michael@0 493 return NULL;
michael@0 494 }
michael@0 495
michael@0 496 dest = data->output_buffer;
michael@0 497
michael@0 498 /* Must clear this or Destroy will free it. */
michael@0 499 data->output_buffer = NULL;
michael@0 500
michael@0 501 *output_destlen = data->output_length;
michael@0 502 status = PL_DestroyBase64Encoder (data, PR_FALSE);
michael@0 503 if (status == PR_FAILURE) {
michael@0 504 PR_Free(dest);
michael@0 505 return NULL;
michael@0 506 }
michael@0 507
michael@0 508 return dest;
michael@0 509 }
michael@0 510
michael@0 511 /*
michael@0 512 * XXX End of base64 encoding code to be moved into NSPR.
michael@0 513 ********************************************************
michael@0 514 */
michael@0 515
michael@0 516 /*
michael@0 517 * This is the beginning of the NSS cover functions. These will
michael@0 518 * provide the interface we want to expose as NSS-ish. For example,
michael@0 519 * they will operate on our Items, do any special handling or checking
michael@0 520 * we want to do, etc.
michael@0 521 */
michael@0 522
michael@0 523
michael@0 524 PR_BEGIN_EXTERN_C
michael@0 525
michael@0 526 /*
michael@0 527 * A boring cover structure for now. Perhaps someday it will include
michael@0 528 * some more interesting fields.
michael@0 529 */
michael@0 530 struct NSSBase64EncoderStr {
michael@0 531 PLBase64Encoder *pl_data;
michael@0 532 };
michael@0 533
michael@0 534 PR_END_EXTERN_C
michael@0 535
michael@0 536
michael@0 537 /*
michael@0 538 * Function to start a base64 encoding context.
michael@0 539 */
michael@0 540 NSSBase64Encoder *
michael@0 541 NSSBase64Encoder_Create (PRInt32 (*output_fn) (void *, const char *, PRInt32),
michael@0 542 void *output_arg)
michael@0 543 {
michael@0 544 PLBase64Encoder *pl_data;
michael@0 545 NSSBase64Encoder *nss_data;
michael@0 546
michael@0 547 nss_data = PORT_ZNew(NSSBase64Encoder);
michael@0 548 if (nss_data == NULL)
michael@0 549 return NULL;
michael@0 550
michael@0 551 pl_data = PL_CreateBase64Encoder (output_fn, output_arg, 64);
michael@0 552 if (pl_data == NULL) {
michael@0 553 PORT_Free(nss_data);
michael@0 554 return NULL;
michael@0 555 }
michael@0 556
michael@0 557 nss_data->pl_data = pl_data;
michael@0 558 return nss_data;
michael@0 559 }
michael@0 560
michael@0 561
michael@0 562 /*
michael@0 563 * Push data through the encoder, causing the output_fn (provided to Create)
michael@0 564 * to be called with the encoded data.
michael@0 565 */
michael@0 566 SECStatus
michael@0 567 NSSBase64Encoder_Update (NSSBase64Encoder *data, const unsigned char *buffer,
michael@0 568 PRUint32 size)
michael@0 569 {
michael@0 570 PRStatus pr_status;
michael@0 571
michael@0 572 /* XXX Should we do argument checking only in debug build? */
michael@0 573 if (data == NULL) {
michael@0 574 PORT_SetError (SEC_ERROR_INVALID_ARGS);
michael@0 575 return SECFailure;
michael@0 576 }
michael@0 577
michael@0 578 pr_status = PL_UpdateBase64Encoder (data->pl_data, buffer, size);
michael@0 579 if (pr_status == PR_FAILURE)
michael@0 580 return SECFailure;
michael@0 581
michael@0 582 return SECSuccess;
michael@0 583 }
michael@0 584
michael@0 585
michael@0 586 /*
michael@0 587 * When you're done encoding, call this to free the data. If "abort_p"
michael@0 588 * is false, then calling this may cause the output_fn to be called
michael@0 589 * one last time (as the last buffered data is flushed out).
michael@0 590 */
michael@0 591 SECStatus
michael@0 592 NSSBase64Encoder_Destroy (NSSBase64Encoder *data, PRBool abort_p)
michael@0 593 {
michael@0 594 PRStatus pr_status;
michael@0 595
michael@0 596 /* XXX Should we do argument checking only in debug build? */
michael@0 597 if (data == NULL) {
michael@0 598 PORT_SetError (SEC_ERROR_INVALID_ARGS);
michael@0 599 return SECFailure;
michael@0 600 }
michael@0 601
michael@0 602 pr_status = PL_DestroyBase64Encoder (data->pl_data, abort_p);
michael@0 603
michael@0 604 PORT_Free(data);
michael@0 605
michael@0 606 if (pr_status == PR_FAILURE)
michael@0 607 return SECFailure;
michael@0 608
michael@0 609 return SECSuccess;
michael@0 610 }
michael@0 611
michael@0 612
michael@0 613 /*
michael@0 614 * Perform base64 encoding of binary data "inItem" to an ascii string.
michael@0 615 * The output buffer may be provided (as "outStrOpt"); you can also pass
michael@0 616 * in a NULL and the buffer will be allocated for you. The result will
michael@0 617 * be null-terminated, and if the buffer is provided, "maxOutLen" must
michael@0 618 * specify the maximum length of the buffer and will be checked to
michael@0 619 * supply sufficient space space for the encoded result. (If "outStrOpt"
michael@0 620 * is NULL, "maxOutLen" is ignored.)
michael@0 621 *
michael@0 622 * If "outStrOpt" is NULL, allocation will happen out of the passed-in
michael@0 623 * "arenaOpt", if *it* is non-NULL, otherwise standard allocation (heap)
michael@0 624 * will be used.
michael@0 625 *
michael@0 626 * Return value is NULL on error, the output buffer (allocated or provided)
michael@0 627 * otherwise.
michael@0 628 */
michael@0 629 char *
michael@0 630 NSSBase64_EncodeItem (PLArenaPool *arenaOpt, char *outStrOpt,
michael@0 631 unsigned int maxOutLen, SECItem *inItem)
michael@0 632 {
michael@0 633 char *out_string = outStrOpt;
michael@0 634 PRUint32 max_out_len;
michael@0 635 PRUint32 out_len;
michael@0 636 void *mark = NULL;
michael@0 637 char *dummy;
michael@0 638
michael@0 639 PORT_Assert(inItem != NULL && inItem->data != NULL && inItem->len != 0);
michael@0 640 if (inItem == NULL || inItem->data == NULL || inItem->len == 0) {
michael@0 641 PORT_SetError (SEC_ERROR_INVALID_ARGS);
michael@0 642 return NULL;
michael@0 643 }
michael@0 644
michael@0 645 max_out_len = PL_Base64MaxEncodedLength (inItem->len, 64);
michael@0 646
michael@0 647 if (arenaOpt != NULL)
michael@0 648 mark = PORT_ArenaMark (arenaOpt);
michael@0 649
michael@0 650 if (out_string == NULL) {
michael@0 651 if (arenaOpt != NULL)
michael@0 652 out_string = PORT_ArenaAlloc (arenaOpt, max_out_len + 1);
michael@0 653 else
michael@0 654 out_string = PORT_Alloc (max_out_len + 1);
michael@0 655
michael@0 656 if (out_string == NULL) {
michael@0 657 if (arenaOpt != NULL)
michael@0 658 PORT_ArenaRelease (arenaOpt, mark);
michael@0 659 return NULL;
michael@0 660 }
michael@0 661 } else {
michael@0 662 if ((max_out_len + 1) > maxOutLen) {
michael@0 663 PORT_SetError (SEC_ERROR_OUTPUT_LEN);
michael@0 664 return NULL;
michael@0 665 }
michael@0 666 max_out_len = maxOutLen;
michael@0 667 }
michael@0 668
michael@0 669
michael@0 670 dummy = PL_Base64EncodeBuffer (inItem->data, inItem->len, 64,
michael@0 671 out_string, max_out_len, &out_len);
michael@0 672 if (dummy == NULL) {
michael@0 673 if (arenaOpt != NULL) {
michael@0 674 PORT_ArenaRelease (arenaOpt, mark);
michael@0 675 } else {
michael@0 676 PORT_Free (out_string);
michael@0 677 }
michael@0 678 return NULL;
michael@0 679 }
michael@0 680
michael@0 681 if (arenaOpt != NULL)
michael@0 682 PORT_ArenaUnmark (arenaOpt, mark);
michael@0 683
michael@0 684 out_string[out_len] = '\0';
michael@0 685 return out_string;
michael@0 686 }
michael@0 687
michael@0 688
michael@0 689 /*
michael@0 690 * XXX Everything below is deprecated. If you add new stuff, put it
michael@0 691 * *above*, not below.
michael@0 692 */
michael@0 693
michael@0 694 /*
michael@0 695 * XXX The following "BTOA" functions are provided for backward compatibility
michael@0 696 * with current code. They should be considered strongly deprecated.
michael@0 697 * When we can convert all our code over to using the new NSSBase64Encoder_
michael@0 698 * functions defined above, we should get rid of these altogether. (Remove
michael@0 699 * protoypes from base64.h as well -- actually, remove that file completely).
michael@0 700 * If someone thinks either of these functions provides such a very useful
michael@0 701 * interface (though, as shown, the same functionality can already be
michael@0 702 * obtained by calling NSSBase64_EncodeItem directly), fine -- but then
michael@0 703 * that API should be provided with a nice new NSSFoo name and using
michael@0 704 * appropriate types, etc.
michael@0 705 */
michael@0 706
michael@0 707 #include "base64.h"
michael@0 708
michael@0 709 /*
michael@0 710 ** Return an PORT_Alloc'd ascii string which is the base64 encoded
michael@0 711 ** version of the input string.
michael@0 712 */
michael@0 713 char *
michael@0 714 BTOA_DataToAscii(const unsigned char *data, unsigned int len)
michael@0 715 {
michael@0 716 SECItem binary_item;
michael@0 717
michael@0 718 binary_item.data = (unsigned char *)data;
michael@0 719 binary_item.len = len;
michael@0 720
michael@0 721 return NSSBase64_EncodeItem (NULL, NULL, 0, &binary_item);
michael@0 722 }
michael@0 723
michael@0 724 /*
michael@0 725 ** Convert from binary encoding of an item to ascii.
michael@0 726 */
michael@0 727 char *
michael@0 728 BTOA_ConvertItemToAscii (SECItem *binary_item)
michael@0 729 {
michael@0 730 return NSSBase64_EncodeItem (NULL, NULL, 0, binary_item);
michael@0 731 }

mercurial