nssb64d.c
来自「支持SSL v2/v3, TLS, PKCS #5, PKCS #7, PKCS」· C语言 代码 · 共 862 行 · 第 1/2 页
C
862 行
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1994-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. *//* * Base64 decoding (ascii to binary). * * $Id: nssb64d.c,v 1.3 2000/04/06 00:38:12 repka%netscape.com Exp $ */#include "nssb64.h"#include "nspr.h"#include "secitem.h"#include "secerr.h"/* * XXX We want this basic support to go into NSPR (the PL part). * Until that can happen, the PL interface is going to be kept entirely * internal here -- all static functions and opaque data structures. * When someone can get it moved over into NSPR, that should be done: * - giving everything names that are accepted by the NSPR module owners * (though I tried to choose ones that would work without modification) * - exporting the functions (remove static declarations and add * PR_IMPLEMENT as necessary) * - put prototypes into appropriate header file (probably replacing * the entire current lib/libc/include/plbase64.h in NSPR) * along with a typedef for the context structure (which should be * kept opaque -- definition in the source file only, but typedef * ala "typedef struct PLBase64FooStr PLBase64Foo;" in header file) * - modify anything else as necessary to conform to NSPR required style * (I looked but found no formatting guide to follow) * * You will want to move over everything from here down to the comment * which says "XXX End of base64 decoding code to be moved into NSPR", * into a new file in NSPR. *//* ************************************************************** * XXX Beginning of base64 decoding code to be moved into NSPR. *//* * This typedef would belong in the NSPR header file (i.e. plbase64.h). */typedef struct PLBase64DecoderStr PLBase64Decoder;/* * The following implementation of base64 decoding was based on code * found in libmime (specifically, in mimeenc.c). It has been adapted to * use PR types and naming as well as to provide other necessary semantics * (like buffer-in/buffer-out in addition to "streaming" without undue * performance hit of extra copying if you made the buffer versions * use the output_fn). It also incorporates some aspects of the current * NSPR base64 decoding code. As such, you may find similarities to * both of those implementations. I tried to use names that reflected * the original code when possible. For this reason you may find some * inconsistencies -- libmime used lots of "in" and "out" whereas the * NSPR version uses "src" and "dest"; sometimes I changed one to the other * and sometimes I left them when I thought the subroutines were at least * self-consistent. */PR_BEGIN_EXTERN_C/* * Opaque object used by the decoder to store state. */struct PLBase64DecoderStr { /* Current token (or portion, if token_size < 4) being decoded. */ unsigned char token[4]; int token_size; /* * Where to write the decoded data (used when streaming, not when * doing all in-memory (buffer) operations). * * Note that this definition is chosen to be compatible with PR_Write. */ PRInt32 (*output_fn) (void *output_arg, const unsigned char *buf, PRInt32 size); void *output_arg; /* * Where the decoded output goes -- either temporarily (in the streaming * case, staged here before it goes to the output function) or what will * be the entire buffered result for users of the buffer version. */ unsigned char *output_buffer; PRUint32 output_buflen; /* the total length of allocated buffer */ PRUint32 output_length; /* the length that is currently populated */};PR_END_EXTERN_C/* * Table to convert an ascii "code" to its corresponding binary value. * For ease of use, the binary values in the table are the actual values * PLUS ONE. This is so that the special value of zero can denote an * invalid mapping; that was much easier than trying to fill in the other * values with some value other than zero, and to check for it. * Just remember to SUBTRACT ONE when using the value retrieved. */static unsigned char base64_codetovaluep1[256] = {/* 0: */ 0, 0, 0, 0, 0, 0, 0, 0,/* 8: */ 0, 0, 0, 0, 0, 0, 0, 0,/* 16: */ 0, 0, 0, 0, 0, 0, 0, 0,/* 24: */ 0, 0, 0, 0, 0, 0, 0, 0,/* 32: */ 0, 0, 0, 0, 0, 0, 0, 0,/* 40: */ 0, 0, 0, 63, 0, 0, 0, 64,/* 48: */ 53, 54, 55, 56, 57, 58, 59, 60,/* 56: */ 61, 62, 0, 0, 0, 0, 0, 0,/* 64: */ 0, 1, 2, 3, 4, 5, 6, 7,/* 72: */ 8, 9, 10, 11, 12, 13, 14, 15,/* 80: */ 16, 17, 18, 19, 20, 21, 22, 23,/* 88: */ 24, 25, 26, 0, 0, 0, 0, 0,/* 96: */ 0, 27, 28, 29, 30, 31, 32, 33,/* 104: */ 34, 35, 36, 37, 38, 39, 40, 41,/* 112: */ 42, 43, 44, 45, 46, 47, 48, 49,/* 120: */ 50, 51, 52, 0, 0, 0, 0, 0,/* 128: */ 0, 0, 0, 0, 0, 0, 0, 0/* and rest are all zero as well */};#define B64_PAD '='/* * Reads 4; writes 3 (known, or expected, to have no trailing padding). * Returns bytes written; -1 on error (unexpected character). */static intpl_base64_decode_4to3 (const unsigned char *in, unsigned char *out){ int j; PRUint32 num = 0; unsigned char bits; for (j = 0; j < 4; j++) { bits = base64_codetovaluep1[in[j]]; if (bits == 0) return -1; num = (num << 6) | (bits - 1); } out[0] = (unsigned char) (num >> 16); out[1] = (unsigned char) ((num >> 8) & 0xFF); out[2] = (unsigned char) (num & 0xFF); return 3;}/* * Reads 3; writes 2 (caller already confirmed EOF or trailing padding). * Returns bytes written; -1 on error (unexpected character). */static intpl_base64_decode_3to2 (const unsigned char *in, unsigned char *out){ PRUint32 num = 0; unsigned char bits1, bits2, bits3; bits1 = base64_codetovaluep1[in[0]]; bits2 = base64_codetovaluep1[in[1]]; bits3 = base64_codetovaluep1[in[2]]; if ((bits1 == 0) || (bits2 == 0) || (bits3 == 0)) return -1; num = ((PRUint32)(bits1 - 1)) << 10; num |= ((PRUint32)(bits2 - 1)) << 4; num |= ((PRUint32)(bits3 - 1)) >> 2; out[0] = (unsigned char) (num >> 8); out[1] = (unsigned char) (num & 0xFF); return 2;}/* * Reads 2; writes 1 (caller already confirmed EOF or trailing padding). * Returns bytes written; -1 on error (unexpected character). */static intpl_base64_decode_2to1 (const unsigned char *in, unsigned char *out){ PRUint32 num = 0; unsigned char bits1, bits2; bits1 = base64_codetovaluep1[in[0]]; bits2 = base64_codetovaluep1[in[1]]; if ((bits1 == 0) || (bits2 == 0)) return -1; num = ((PRUint32)(bits1 - 1)) << 2; num |= ((PRUint32)(bits2 - 1)) >> 4; out[0] = (unsigned char) num; return 1;}/* * Reads 4; writes 0-3. Returns bytes written or -1 on error. * (Writes less than 3 only at (presumed) EOF.) */static intpl_base64_decode_token (const unsigned char *in, unsigned char *out){ if (in[3] != B64_PAD) return pl_base64_decode_4to3 (in, out); if (in[2] == B64_PAD) return pl_base64_decode_2to1 (in, out); return pl_base64_decode_3to2 (in, out);}static PRStatuspl_base64_decode_buffer (PLBase64Decoder *data, const unsigned char *in, PRUint32 length){ unsigned char *out = data->output_buffer; unsigned char *token = data->token; int i, n = 0; i = data->token_size; data->token_size = 0; while (length > 0) { while (i < 4 && length > 0) { /* * XXX Note that the following simply ignores any unexpected * characters. This is exactly what the original code in * libmime did, and I am leaving it. We certainly want to skip * over whitespace (we must); this does much more than that. * I am not confident changing it, and I don't want to slow * the processing down doing more complicated checking, but * someone else might have different ideas in the future. */ if (base64_codetovaluep1[*in] > 0 || *in == B64_PAD) token[i++] = *in; in++; length--; } if (i < 4) { /* Didn't get enough for a complete token. */ data->token_size = i; break; } i = 0; PR_ASSERT((out - data->output_buffer + 3) <= data->output_buflen); /* * Assume we are not at the end; the following function only works * for an internal token (no trailing padding characters) but is * faster that way. If it hits an invalid character (padding) it * will return an error; we break out of the loop and try again * calling the routine that will handle a final token. * Note that we intentionally do it this way rather than explicitly * add a check for padding here (because that would just slow down * the normal case) nor do we rely on checking whether we have more * input to process (because that would also slow it down but also * because we want to allow trailing garbage, especially white space * and cannot tell that without read-ahead, also a slow proposition). * Whew. Understand? */ n = pl_base64_decode_4to3 (token, out); if (n < 0) break; /* Advance "out" by the number of bytes just written to it. */ out += n; n = 0; } /* * See big comment above, before call to pl_base64_decode_4to3. * Here we check if we error'd out of loop, and allow for the case * that we are processing the last interesting token. If the routine * which should handle padding characters also fails, then we just * have bad input and give up. */ if (n < 0) { n = pl_base64_decode_token (token, out); if (n < 0) return PR_FAILURE; out += n; } /* * As explained above, we can get here with more input remaining, but * it should be all characters we do not care about (i.e. would be * ignored when transferring from "in" to "token" in loop above, * except here we choose to ignore extraneous pad characters, too). * Swallow it, performing that check. If we find more characters that * we would expect to decode, something is wrong. */ while (length > 0) { if (base64_codetovaluep1[*in] > 0) return PR_FAILURE; in++; length--; } /* Record the length of decoded data we have left in output_buffer. */ data->output_length = (PRUint32) (out - data->output_buffer); return PR_SUCCESS;}/* * Flush any remaining buffered characters. Given well-formed input, * this will have nothing to do. If the input was missing the padding * characters at the end, though, there could be 1-3 characters left * behind -- we will tolerate that by adding the padding for them. */static PRStatuspl_base64_decode_flush (PLBase64Decoder *data){ int count; /* * If no remaining characters, or all are padding (also not well-formed * input, but again, be tolerant), then nothing more to do. (And, that * is considered successful.) */ if (data->token_size == 0 || data->token[0] == B64_PAD) return PR_SUCCESS; /* * Assume we have all the interesting input except for some expected * padding characters. Add them and decode the resulting token. */ while (data->token_size < 4) data->token[data->token_size++] = B64_PAD; data->token_size = 0; /* so a subsequent flush call is a no-op */ count = pl_base64_decode_token (data->token, data->output_buffer + data->output_length); if (count < 0) return PR_FAILURE; /* * If there is an output function, call it with this last bit of data. * Otherwise we are doing all buffered output, and the decoded bytes * are now there, we just need to reflect that in the length. */ if (data->output_fn != NULL) { PRInt32 output_result; PR_ASSERT(data->output_length == 0); output_result = data->output_fn (data->output_arg, data->output_buffer, (PRInt32) count); if (output_result < 0) return PR_FAILURE; } else { data->output_length += count; } return PR_SUCCESS;}/* * The maximum space needed to hold the output of the decoder given * input data of length "size". */static PRUint32PL_Base64MaxDecodedLength (PRUint32 size){ return ((size * 3) / 4);}/* * A distinct internal creation function for the buffer version to use. * (It does not want to specify an output_fn, and we want the normal * Create function to require that.) If more common initialization * of the decoding context needs to be done, it should be done *here*. */static PLBase64Decoder *pl_base64_create_decoder (void){ return PR_NEWZAP(PLBase64Decoder);}/* * Function to start a base64 decoding context. * An "output_fn" is required; the "output_arg" parameter to that is optional. */static PLBase64Decoder *PL_CreateBase64Decoder (PRInt32 (*output_fn) (void *, const unsigned char *, PRInt32), void *output_arg){
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?