security/nss/lib/util/nssb64e.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/lib/util/nssb64e.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,731 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +/*
     1.9 + * Base64 encoding (binary to ascii).
    1.10 + */
    1.11 +
    1.12 +#include "nssb64.h"
    1.13 +#include "nspr.h"
    1.14 +#include "secitem.h"
    1.15 +#include "secerr.h"
    1.16 +
    1.17 +/*
    1.18 + * XXX See the big comment at the top of nssb64d.c about moving the
    1.19 + * bulk of this code over into NSPR (the PL part).  It all applies
    1.20 + * here but I didn't want to duplicate it, to avoid divergence problems.
    1.21 + */ 
    1.22 +
    1.23 +/*
    1.24 + **************************************************************
    1.25 + * XXX Beginning of base64 encoding code to be moved into NSPR.
    1.26 + */
    1.27 +
    1.28 +
    1.29 +struct PLBase64EncodeStateStr {
    1.30 +    unsigned chunks;
    1.31 +    unsigned saved;
    1.32 +    unsigned char buf[3];
    1.33 +};
    1.34 +
    1.35 +/*
    1.36 + * This typedef would belong in the NSPR header file (i.e. plbase64.h).
    1.37 + */
    1.38 +typedef struct PLBase64EncoderStr PLBase64Encoder;
    1.39 +
    1.40 +/*
    1.41 + * The following implementation of base64 encoding was based on code
    1.42 + * found in libmime (specifically, in mimeenc.c).  It has been adapted to
    1.43 + * use PR types and naming as well as to provide other necessary semantics
    1.44 + * (like buffer-in/buffer-out in addition to "streaming" without undue
    1.45 + * performance hit of extra copying if you made the buffer versions
    1.46 + * use the output_fn).  It also incorporates some aspects of the current
    1.47 + * NSPR base64 encoding code.  As such, you may find similarities to
    1.48 + * both of those implementations.  I tried to use names that reflected
    1.49 + * the original code when possible.  For this reason you may find some
    1.50 + * inconsistencies -- libmime used lots of "in" and "out" whereas the
    1.51 + * NSPR version uses "src" and "dest"; sometimes I changed one to the other
    1.52 + * and sometimes I left them when I thought the subroutines were at least
    1.53 + * self-consistent.
    1.54 + */
    1.55 +
    1.56 +PR_BEGIN_EXTERN_C
    1.57 +
    1.58 +/*
    1.59 + * Opaque object used by the encoder to store state.
    1.60 + */
    1.61 +struct PLBase64EncoderStr {
    1.62 +    /*
    1.63 +     * The one or two bytes pending.  (We need 3 to create a "token",
    1.64 +     * and hold the leftovers here.  in_buffer_count is *only* ever
    1.65 +     * 0, 1, or 2.
    1.66 +     */
    1.67 +    unsigned char in_buffer[2];
    1.68 +    int in_buffer_count;
    1.69 +
    1.70 +    /*
    1.71 +     * If the caller wants linebreaks added, line_length specifies
    1.72 +     * where they come out.  It must be a multiple of 4; if the caller
    1.73 +     * provides one that isn't, we round it down to the nearest
    1.74 +     * multiple of 4.
    1.75 +     *
    1.76 +     * The value of current_column counts how many characters have been
    1.77 +     * added since the last linebreaks (or since the beginning, on the
    1.78 +     * first line).  It is also always a multiple of 4; it is unused when
    1.79 +     * line_length is 0.
    1.80 +     */ 
    1.81 +    PRUint32 line_length;
    1.82 +    PRUint32 current_column;
    1.83 +
    1.84 +    /*
    1.85 +     * Where to write the encoded data (used when streaming, not when
    1.86 +     * doing all in-memory (buffer) operations).
    1.87 +     *
    1.88 +     * Note that this definition is chosen to be compatible with PR_Write.
    1.89 +     */
    1.90 +    PRInt32 (*output_fn) (void *output_arg, const char *buf, PRInt32 size);
    1.91 +    void *output_arg;
    1.92 +
    1.93 +    /*
    1.94 +     * Where the encoded output goes -- either temporarily (in the streaming
    1.95 +     * case, staged here before it goes to the output function) or what will
    1.96 +     * be the entire buffered result for users of the buffer version.
    1.97 +     */
    1.98 +    char *output_buffer;
    1.99 +    PRUint32 output_buflen;	/* the total length of allocated buffer */
   1.100 +    PRUint32 output_length;	/* the length that is currently populated */
   1.101 +};
   1.102 +
   1.103 +PR_END_EXTERN_C
   1.104 +
   1.105 +
   1.106 +/*
   1.107 + * Table to convert a binary value to its corresponding ascii "code".
   1.108 + */
   1.109 +static unsigned char base64_valuetocode[64] =
   1.110 +    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
   1.111 +
   1.112 +#define B64_PAD	'='
   1.113 +#define B64_CR	'\r'
   1.114 +#define B64_LF	'\n'
   1.115 +
   1.116 +static PRStatus
   1.117 +pl_base64_encode_buffer (PLBase64Encoder *data, const unsigned char *in,
   1.118 +			 PRUint32 size)
   1.119 +{
   1.120 +    const unsigned char *end = in + size;
   1.121 +    char *out = data->output_buffer + data->output_length;
   1.122 +    unsigned int i = data->in_buffer_count;
   1.123 +    PRUint32 n = 0;
   1.124 +    int off;
   1.125 +    PRUint32 output_threshold;
   1.126 +
   1.127 +    /* If this input buffer is too small, wait until next time. */
   1.128 +    if (size < (3 - i)) {
   1.129 +	data->in_buffer[i++] = in[0];
   1.130 +	if (size > 1)
   1.131 +	    data->in_buffer[i++] = in[1];
   1.132 +	PR_ASSERT(i < 3);
   1.133 +	data->in_buffer_count = i;
   1.134 +	return PR_SUCCESS;
   1.135 +    }
   1.136 +
   1.137 +    /* If there are bytes that were put back last time, take them now. */
   1.138 +    if (i > 0) {
   1.139 +	n = data->in_buffer[0];
   1.140 +	if (i > 1)
   1.141 +	    n = (n << 8) | data->in_buffer[1];
   1.142 +	data->in_buffer_count = 0;
   1.143 +    }
   1.144 +
   1.145 +    /* If our total is not a multiple of three, put one or two bytes back. */
   1.146 +    off = (size + i) % 3;
   1.147 +    if (off > 0) {
   1.148 +	size -= off;
   1.149 +	data->in_buffer[0] = in[size];
   1.150 +	if (off > 1)
   1.151 +	    data->in_buffer[1] = in[size + 1];
   1.152 +	data->in_buffer_count = off;
   1.153 +	end -= off;
   1.154 +    }
   1.155 +
   1.156 +    output_threshold = data->output_buflen - 3;
   1.157 +
   1.158 +    /*
   1.159 +     * Populate the output buffer with base64 data, one line (or buffer)
   1.160 +     * at a time.
   1.161 +     */
   1.162 +    while (in < end) {
   1.163 +	int j, k;
   1.164 +
   1.165 +	while (i < 3) {
   1.166 +	    n = (n << 8) | *in++;
   1.167 +	    i++;
   1.168 +	}
   1.169 +	i = 0;
   1.170 +
   1.171 +	if (data->line_length > 0) {
   1.172 +	    if (data->current_column >= data->line_length) {
   1.173 +		data->current_column = 0;
   1.174 +		*out++ = B64_CR;
   1.175 +		*out++ = B64_LF;
   1.176 +		data->output_length += 2;
   1.177 +	    }
   1.178 +	    data->current_column += 4;	/* the bytes we are about to add */
   1.179 +	}
   1.180 +
   1.181 +	for (j = 18; j >= 0; j -= 6) {
   1.182 +	    k = (n >> j) & 0x3F;
   1.183 +	    *out++ = base64_valuetocode[k];
   1.184 +	}
   1.185 +	n = 0;
   1.186 +	data->output_length += 4;
   1.187 +
   1.188 +	if (data->output_length >= output_threshold) {
   1.189 +	    PR_ASSERT(data->output_length <= data->output_buflen);
   1.190 +	    if (data->output_fn != NULL) {
   1.191 +		PRInt32 output_result;
   1.192 +
   1.193 +		output_result = data->output_fn (data->output_arg,
   1.194 +						 data->output_buffer,
   1.195 +						 (PRInt32) data->output_length);
   1.196 +		if (output_result < 0)
   1.197 +		    return PR_FAILURE;
   1.198 +
   1.199 +		out = data->output_buffer;
   1.200 +		data->output_length = 0;
   1.201 +	    } else {
   1.202 +		/*
   1.203 +		 * Check that we are about to exit the loop.  (Since we
   1.204 +		 * are over the threshold, there isn't enough room in the
   1.205 +		 * output buffer for another trip around.)
   1.206 +		 */
   1.207 +		PR_ASSERT(in == end);
   1.208 +		if (in < end) {
   1.209 +		    PR_SetError (PR_BUFFER_OVERFLOW_ERROR, 0);
   1.210 +		    return PR_FAILURE;
   1.211 +		}
   1.212 +	    }
   1.213 +	}
   1.214 +    }
   1.215 +
   1.216 +    return PR_SUCCESS;
   1.217 +}
   1.218 +
   1.219 +static PRStatus
   1.220 +pl_base64_encode_flush (PLBase64Encoder *data)
   1.221 +{
   1.222 +    int i = data->in_buffer_count;
   1.223 +
   1.224 +    if (i == 0 && data->output_length == 0)
   1.225 +	return PR_SUCCESS;
   1.226 +
   1.227 +    if (i > 0) {
   1.228 +	char *out = data->output_buffer + data->output_length;
   1.229 +	PRUint32 n;
   1.230 +	int j, k;
   1.231 +
   1.232 +	n = ((PRUint32) data->in_buffer[0]) << 16;
   1.233 +	if (i > 1)
   1.234 +	    n |= ((PRUint32) data->in_buffer[1] << 8);
   1.235 +
   1.236 +	data->in_buffer_count = 0;
   1.237 +
   1.238 +	if (data->line_length > 0) {
   1.239 +	    if (data->current_column >= data->line_length) {
   1.240 +		data->current_column = 0;
   1.241 +		*out++ = B64_CR;
   1.242 +		*out++ = B64_LF;
   1.243 +		data->output_length += 2;
   1.244 +	    }
   1.245 +	}
   1.246 +
   1.247 +	/*
   1.248 +	 * This will fill in more than we really have data for, but the
   1.249 +	 * valid parts will end up in the correct position and the extras
   1.250 +	 * will be over-written with pad characters below.
   1.251 +	 */
   1.252 +	for (j = 18; j >= 0; j -= 6) {
   1.253 +	    k = (n >> j) & 0x3F;
   1.254 +	    *out++ = base64_valuetocode[k];
   1.255 +	}
   1.256 +
   1.257 +	/* Pad with equal-signs. */
   1.258 +	if (i == 1)
   1.259 +	    out[-2] = B64_PAD;
   1.260 +	out[-1] = B64_PAD;
   1.261 +
   1.262 +	data->output_length += 4;
   1.263 +    }
   1.264 +
   1.265 +    if (data->output_fn != NULL) {
   1.266 +	PRInt32 output_result;
   1.267 +
   1.268 +	output_result = data->output_fn (data->output_arg, data->output_buffer,
   1.269 +					 (PRInt32) data->output_length);
   1.270 +	data->output_length = 0;
   1.271 +
   1.272 +	if (output_result < 0)
   1.273 +	    return PR_FAILURE;
   1.274 +    }
   1.275 +
   1.276 +    return PR_SUCCESS;
   1.277 +}
   1.278 +
   1.279 +
   1.280 +/*
   1.281 + * The maximum space needed to hold the output of the encoder given input
   1.282 + * data of length "size", and allowing for CRLF added at least every
   1.283 + * line_length bytes (we will add it at nearest lower multiple of 4).
   1.284 + * There is no trailing CRLF.
   1.285 + */
   1.286 +static PRUint32
   1.287 +PL_Base64MaxEncodedLength (PRUint32 size, PRUint32 line_length)
   1.288 +{
   1.289 +    PRUint32 tokens, tokens_per_line, full_lines, line_break_chars, remainder;
   1.290 +
   1.291 +    tokens = (size + 2) / 3;
   1.292 +
   1.293 +    if (line_length == 0)
   1.294 +	return tokens * 4;
   1.295 +
   1.296 +    if (line_length < 4)	/* too small! */
   1.297 +	line_length = 4;
   1.298 +
   1.299 +    tokens_per_line = line_length / 4;
   1.300 +    full_lines = tokens / tokens_per_line;
   1.301 +    remainder = (tokens - (full_lines * tokens_per_line)) * 4;
   1.302 +    line_break_chars = full_lines * 2;
   1.303 +    if (remainder == 0)
   1.304 +	line_break_chars -= 2;
   1.305 +
   1.306 +    return (full_lines * tokens_per_line * 4) + line_break_chars + remainder;
   1.307 +}
   1.308 +
   1.309 +
   1.310 +/*
   1.311 + * A distinct internal creation function for the buffer version to use.
   1.312 + * (It does not want to specify an output_fn, and we want the normal
   1.313 + * Create function to require that.)  All common initialization of the
   1.314 + * encoding context should be done *here*.
   1.315 + *
   1.316 + * Save "line_length", rounded down to nearest multiple of 4 (if not
   1.317 + * already even multiple).  Allocate output_buffer, if not provided --
   1.318 + * based on given size if specified, otherwise based on line_length.
   1.319 + */
   1.320 +static PLBase64Encoder *
   1.321 +pl_base64_create_encoder (PRUint32 line_length, char *output_buffer,
   1.322 +			  PRUint32 output_buflen)
   1.323 +{
   1.324 +    PLBase64Encoder *data;
   1.325 +    PRUint32 line_tokens;
   1.326 +
   1.327 +    data = PR_NEWZAP(PLBase64Encoder);
   1.328 +    if (data == NULL)
   1.329 +	return NULL;
   1.330 +
   1.331 +    if (line_length > 0 && line_length < 4)	/* too small! */
   1.332 +	line_length = 4;
   1.333 +
   1.334 +    line_tokens = line_length / 4;
   1.335 +    data->line_length = line_tokens * 4;
   1.336 +
   1.337 +    if (output_buffer == NULL) {
   1.338 +	if (output_buflen == 0) {
   1.339 +	    if (data->line_length > 0)	/* need to include room for CRLF */
   1.340 +		output_buflen = data->line_length + 2;
   1.341 +	    else
   1.342 +		output_buflen = 64;		/* XXX what is a good size? */
   1.343 +	}
   1.344 +
   1.345 +	output_buffer = (char *) PR_Malloc(output_buflen);
   1.346 +	if (output_buffer == NULL) {
   1.347 +	    PR_Free(data);
   1.348 +	    return NULL;
   1.349 +	}
   1.350 +    }
   1.351 +
   1.352 +    data->output_buffer = output_buffer;
   1.353 +    data->output_buflen = output_buflen;
   1.354 +    return data;
   1.355 +}
   1.356 +
   1.357 +/*
   1.358 + * Function to start a base64 encoding context.
   1.359 + * An "output_fn" is required; the "output_arg" parameter to that is optional.
   1.360 + * If linebreaks in the encoded output are desired, "line_length" specifies
   1.361 + * where to place them -- it will be rounded down to the nearest multiple of 4
   1.362 + * (if it is not already an even multiple of 4).  If it is zero, no linebreaks
   1.363 + * will be added.  (FYI, a linebreak is CRLF -- two characters.)
   1.364 + */
   1.365 +static PLBase64Encoder *
   1.366 +PL_CreateBase64Encoder (PRInt32 (*output_fn) (void *, const char *, PRInt32),
   1.367 +			void *output_arg, PRUint32 line_length)
   1.368 +{
   1.369 +    PLBase64Encoder *data;
   1.370 +
   1.371 +    if (output_fn == NULL) {
   1.372 +	PR_SetError (PR_INVALID_ARGUMENT_ERROR, 0);
   1.373 +	return NULL;
   1.374 +    }
   1.375 +
   1.376 +    data = pl_base64_create_encoder (line_length, NULL, 0);
   1.377 +    if (data == NULL)
   1.378 +	return NULL;
   1.379 +
   1.380 +    data->output_fn = output_fn;
   1.381 +    data->output_arg = output_arg;
   1.382 +
   1.383 +    return data;
   1.384 +}
   1.385 +
   1.386 +
   1.387 +/*
   1.388 + * Push data through the encoder, causing the output_fn (provided to Create)
   1.389 + * to be called with the encoded data.
   1.390 + */
   1.391 +static PRStatus
   1.392 +PL_UpdateBase64Encoder (PLBase64Encoder *data, const unsigned char *buffer,
   1.393 +			PRUint32 size)
   1.394 +{
   1.395 +    /* XXX Should we do argument checking only in debug build? */
   1.396 +    if (data == NULL || buffer == NULL || size == 0) {
   1.397 +	PR_SetError (PR_INVALID_ARGUMENT_ERROR, 0);
   1.398 +	return PR_FAILURE;
   1.399 +    }
   1.400 +
   1.401 +    return pl_base64_encode_buffer (data, buffer, size);
   1.402 +}
   1.403 +
   1.404 +
   1.405 +/*
   1.406 + * When you're done encoding, call this to free the data.  If "abort_p"
   1.407 + * is false, then calling this may cause the output_fn to be called
   1.408 + * one last time (as the last buffered data is flushed out).
   1.409 + */
   1.410 +static PRStatus
   1.411 +PL_DestroyBase64Encoder (PLBase64Encoder *data, PRBool abort_p)
   1.412 +{
   1.413 +    PRStatus status = PR_SUCCESS;
   1.414 +
   1.415 +    /* XXX Should we do argument checking only in debug build? */
   1.416 +    if (data == NULL) {
   1.417 +	PR_SetError (PR_INVALID_ARGUMENT_ERROR, 0);
   1.418 +	return PR_FAILURE;
   1.419 +    }
   1.420 +
   1.421 +    /* Flush out the last few buffered characters. */
   1.422 +    if (!abort_p)
   1.423 +	status = pl_base64_encode_flush (data);
   1.424 +
   1.425 +    if (data->output_buffer != NULL)
   1.426 +	PR_Free(data->output_buffer);
   1.427 +    PR_Free(data);
   1.428 +
   1.429 +    return status;
   1.430 +}
   1.431 +
   1.432 +
   1.433 +/*
   1.434 + * Perform base64 encoding from an input buffer to an output buffer.
   1.435 + * The output buffer can be provided (as "dest"); you can also pass in
   1.436 + * a NULL and this function will allocate a buffer large enough for you,
   1.437 + * and return it.  If you do provide the output buffer, you must also
   1.438 + * provide the maximum length of that buffer (as "maxdestlen").
   1.439 + * The actual encoded length of output will be returned to you in
   1.440 + * "output_destlen".
   1.441 + *
   1.442 + * If linebreaks in the encoded output are desired, "line_length" specifies
   1.443 + * where to place them -- it will be rounded down to the nearest multiple of 4
   1.444 + * (if it is not already an even multiple of 4).  If it is zero, no linebreaks
   1.445 + * will be added.  (FYI, a linebreak is CRLF -- two characters.)
   1.446 + *
   1.447 + * Return value is NULL on error, the output buffer (allocated or provided)
   1.448 + * otherwise.
   1.449 + */
   1.450 +static char *
   1.451 +PL_Base64EncodeBuffer (const unsigned char *src, PRUint32 srclen,
   1.452 +		       PRUint32 line_length, char *dest, PRUint32 maxdestlen,
   1.453 +		       PRUint32 *output_destlen)
   1.454 +{
   1.455 +    PRUint32 need_length;
   1.456 +    PLBase64Encoder *data = NULL;
   1.457 +    PRStatus status;
   1.458 +
   1.459 +    PR_ASSERT(srclen > 0);
   1.460 +    if (srclen == 0)
   1.461 +	return dest;
   1.462 +
   1.463 +    /*
   1.464 +     * How much space could we possibly need for encoding this input?
   1.465 +     */
   1.466 +    need_length = PL_Base64MaxEncodedLength (srclen, line_length);
   1.467 +
   1.468 +    /*
   1.469 +     * Make sure we have at least that much, if output buffer provided.
   1.470 +     */
   1.471 +    if (dest != NULL) {
   1.472 +	PR_ASSERT(maxdestlen >= need_length);
   1.473 +	if (maxdestlen < need_length) {
   1.474 +	    PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
   1.475 +	    return NULL;
   1.476 +	}
   1.477 +    } else {
   1.478 +	maxdestlen = need_length;
   1.479 +    }
   1.480 +
   1.481 +    data = pl_base64_create_encoder(line_length, dest, maxdestlen);
   1.482 +    if (data == NULL)
   1.483 +	return NULL;
   1.484 +
   1.485 +    status = pl_base64_encode_buffer (data, src, srclen);
   1.486 +
   1.487 +    /*
   1.488 +     * We do not wait for Destroy to flush, because Destroy will also
   1.489 +     * get rid of our encoder context, which we need to look at first!
   1.490 +     */
   1.491 +    if (status == PR_SUCCESS)
   1.492 +	status = pl_base64_encode_flush (data);
   1.493 +
   1.494 +    if (status != PR_SUCCESS) {
   1.495 +	(void) PL_DestroyBase64Encoder (data, PR_TRUE);
   1.496 +	return NULL;
   1.497 +    }
   1.498 +
   1.499 +    dest = data->output_buffer;
   1.500 +
   1.501 +    /* Must clear this or Destroy will free it. */
   1.502 +    data->output_buffer = NULL;
   1.503 +
   1.504 +    *output_destlen = data->output_length;
   1.505 +    status = PL_DestroyBase64Encoder (data, PR_FALSE);
   1.506 +    if (status == PR_FAILURE) {
   1.507 +	PR_Free(dest);
   1.508 +	return NULL;
   1.509 +    }
   1.510 +
   1.511 +    return dest;
   1.512 +}
   1.513 +
   1.514 +/*
   1.515 + * XXX End of base64 encoding code to be moved into NSPR.
   1.516 + ********************************************************
   1.517 + */
   1.518 +
   1.519 +/*
   1.520 + * This is the beginning of the NSS cover functions.  These will
   1.521 + * provide the interface we want to expose as NSS-ish.  For example,
   1.522 + * they will operate on our Items, do any special handling or checking
   1.523 + * we want to do, etc.
   1.524 + */
   1.525 +
   1.526 +
   1.527 +PR_BEGIN_EXTERN_C
   1.528 +
   1.529 +/*
   1.530 + * A boring cover structure for now.  Perhaps someday it will include
   1.531 + * some more interesting fields.
   1.532 + */
   1.533 +struct NSSBase64EncoderStr {
   1.534 +    PLBase64Encoder *pl_data;
   1.535 +};
   1.536 +
   1.537 +PR_END_EXTERN_C
   1.538 +
   1.539 +
   1.540 +/*
   1.541 + * Function to start a base64 encoding context.
   1.542 + */
   1.543 +NSSBase64Encoder *
   1.544 +NSSBase64Encoder_Create (PRInt32 (*output_fn) (void *, const char *, PRInt32),
   1.545 +			 void *output_arg)
   1.546 +{
   1.547 +    PLBase64Encoder *pl_data;
   1.548 +    NSSBase64Encoder *nss_data;
   1.549 +
   1.550 +    nss_data = PORT_ZNew(NSSBase64Encoder);
   1.551 +    if (nss_data == NULL)
   1.552 +	return NULL;
   1.553 +
   1.554 +    pl_data = PL_CreateBase64Encoder (output_fn, output_arg, 64);
   1.555 +    if (pl_data == NULL) {
   1.556 +	PORT_Free(nss_data);
   1.557 +	return NULL;
   1.558 +    }
   1.559 +
   1.560 +    nss_data->pl_data = pl_data;
   1.561 +    return nss_data;
   1.562 +}
   1.563 +
   1.564 +
   1.565 +/*
   1.566 + * Push data through the encoder, causing the output_fn (provided to Create)
   1.567 + * to be called with the encoded data.
   1.568 + */
   1.569 +SECStatus
   1.570 +NSSBase64Encoder_Update (NSSBase64Encoder *data, const unsigned char *buffer,
   1.571 +			 PRUint32 size)
   1.572 +{
   1.573 +    PRStatus pr_status;
   1.574 +
   1.575 +    /* XXX Should we do argument checking only in debug build? */
   1.576 +    if (data == NULL) {
   1.577 +	PORT_SetError (SEC_ERROR_INVALID_ARGS);
   1.578 +	return SECFailure;
   1.579 +    }
   1.580 +
   1.581 +    pr_status = PL_UpdateBase64Encoder (data->pl_data, buffer, size);
   1.582 +    if (pr_status == PR_FAILURE)
   1.583 +	return SECFailure;
   1.584 +
   1.585 +    return SECSuccess;
   1.586 +}
   1.587 +
   1.588 +
   1.589 +/*
   1.590 + * When you're done encoding, call this to free the data.  If "abort_p"
   1.591 + * is false, then calling this may cause the output_fn to be called
   1.592 + * one last time (as the last buffered data is flushed out).
   1.593 + */
   1.594 +SECStatus
   1.595 +NSSBase64Encoder_Destroy (NSSBase64Encoder *data, PRBool abort_p)
   1.596 +{
   1.597 +    PRStatus pr_status;
   1.598 +
   1.599 +    /* XXX Should we do argument checking only in debug build? */
   1.600 +    if (data == NULL) {
   1.601 +	PORT_SetError (SEC_ERROR_INVALID_ARGS);
   1.602 +	return SECFailure;
   1.603 +    }
   1.604 +
   1.605 +    pr_status = PL_DestroyBase64Encoder (data->pl_data, abort_p);
   1.606 +
   1.607 +    PORT_Free(data);
   1.608 +
   1.609 +    if (pr_status == PR_FAILURE)
   1.610 +	return SECFailure;
   1.611 +
   1.612 +    return SECSuccess;
   1.613 +}
   1.614 +
   1.615 +
   1.616 +/*
   1.617 + * Perform base64 encoding of binary data "inItem" to an ascii string.
   1.618 + * The output buffer may be provided (as "outStrOpt"); you can also pass
   1.619 + * in a NULL and the buffer will be allocated for you.  The result will
   1.620 + * be null-terminated, and if the buffer is provided, "maxOutLen" must
   1.621 + * specify the maximum length of the buffer and will be checked to
   1.622 + * supply sufficient space space for the encoded result.  (If "outStrOpt"
   1.623 + * is NULL, "maxOutLen" is ignored.)
   1.624 + *
   1.625 + * If "outStrOpt" is NULL, allocation will happen out of the passed-in
   1.626 + * "arenaOpt", if *it* is non-NULL, otherwise standard allocation (heap)
   1.627 + * will be used.
   1.628 + *
   1.629 + * Return value is NULL on error, the output buffer (allocated or provided)
   1.630 + * otherwise.
   1.631 + */
   1.632 +char *
   1.633 +NSSBase64_EncodeItem (PLArenaPool *arenaOpt, char *outStrOpt,
   1.634 +		      unsigned int maxOutLen, SECItem *inItem)
   1.635 +{
   1.636 +    char *out_string = outStrOpt;
   1.637 +    PRUint32 max_out_len;
   1.638 +    PRUint32 out_len;
   1.639 +    void *mark = NULL;
   1.640 +    char *dummy;
   1.641 +
   1.642 +    PORT_Assert(inItem != NULL && inItem->data != NULL && inItem->len != 0);
   1.643 +    if (inItem == NULL || inItem->data == NULL || inItem->len == 0) {
   1.644 +	PORT_SetError (SEC_ERROR_INVALID_ARGS);
   1.645 +	return NULL;
   1.646 +    }
   1.647 +
   1.648 +    max_out_len = PL_Base64MaxEncodedLength (inItem->len, 64);
   1.649 +
   1.650 +    if (arenaOpt != NULL)
   1.651 +	mark = PORT_ArenaMark (arenaOpt);
   1.652 +
   1.653 +    if (out_string == NULL) {
   1.654 +	if (arenaOpt != NULL)
   1.655 +	    out_string = PORT_ArenaAlloc (arenaOpt, max_out_len + 1);
   1.656 +	else
   1.657 +	    out_string = PORT_Alloc (max_out_len + 1);
   1.658 +
   1.659 +	if (out_string == NULL) {
   1.660 +	    if (arenaOpt != NULL)
   1.661 +		PORT_ArenaRelease (arenaOpt, mark);
   1.662 +	    return NULL;
   1.663 +	}
   1.664 +    } else {
   1.665 +	if ((max_out_len + 1) > maxOutLen) {
   1.666 +	    PORT_SetError (SEC_ERROR_OUTPUT_LEN);
   1.667 +	    return NULL;
   1.668 +	}
   1.669 +	max_out_len = maxOutLen;
   1.670 +    }
   1.671 +
   1.672 +
   1.673 +    dummy = PL_Base64EncodeBuffer (inItem->data, inItem->len, 64,
   1.674 +				   out_string, max_out_len, &out_len);
   1.675 +    if (dummy == NULL) {
   1.676 +	if (arenaOpt != NULL) {
   1.677 +	    PORT_ArenaRelease (arenaOpt, mark);
   1.678 +	} else {
   1.679 +	    PORT_Free (out_string);
   1.680 +	}
   1.681 +	return NULL;
   1.682 +    }
   1.683 +
   1.684 +    if (arenaOpt != NULL)
   1.685 +	PORT_ArenaUnmark (arenaOpt, mark);
   1.686 +
   1.687 +    out_string[out_len] = '\0';
   1.688 +    return out_string;
   1.689 +}
   1.690 +
   1.691 +
   1.692 +/*
   1.693 + * XXX Everything below is deprecated.  If you add new stuff, put it
   1.694 + * *above*, not below.
   1.695 + */
   1.696 +
   1.697 +/*
   1.698 + * XXX The following "BTOA" functions are provided for backward compatibility
   1.699 + * with current code.  They should be considered strongly deprecated.
   1.700 + * When we can convert all our code over to using the new NSSBase64Encoder_
   1.701 + * functions defined above, we should get rid of these altogether.  (Remove
   1.702 + * protoypes from base64.h as well -- actually, remove that file completely).
   1.703 + * If someone thinks either of these functions provides such a very useful
   1.704 + * interface (though, as shown, the same functionality can already be
   1.705 + * obtained by calling NSSBase64_EncodeItem directly), fine -- but then
   1.706 + * that API should be provided with a nice new NSSFoo name and using
   1.707 + * appropriate types, etc.
   1.708 + */
   1.709 +
   1.710 +#include "base64.h"
   1.711 +
   1.712 +/*
   1.713 +** Return an PORT_Alloc'd ascii string which is the base64 encoded
   1.714 +** version of the input string.
   1.715 +*/
   1.716 +char *
   1.717 +BTOA_DataToAscii(const unsigned char *data, unsigned int len)
   1.718 +{
   1.719 +    SECItem binary_item;
   1.720 +
   1.721 +    binary_item.data = (unsigned char *)data;
   1.722 +    binary_item.len = len;
   1.723 +
   1.724 +    return NSSBase64_EncodeItem (NULL, NULL, 0, &binary_item);
   1.725 +}
   1.726 +
   1.727 +/*
   1.728 +** Convert from binary encoding of an item to ascii.
   1.729 +*/
   1.730 +char *
   1.731 +BTOA_ConvertItemToAscii (SECItem *binary_item)
   1.732 +{
   1.733 +    return NSSBase64_EncodeItem (NULL, NULL, 0, binary_item);
   1.734 +}

mercurial