X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=blobdiff_plain;f=crypto%2Fevp%2Fencode.c;h=91160adec06c31d5049333bfe50e395a711c7009;hp=53cc5863968152e1f10a0d7d0837ac7a84d4c863;hb=a0be4fd17b1c7f5ab8f8e11c71d71a5dd20158f4;hpb=0f113f3ee4d629ef9a4a30911b22b224772085e5 diff --git a/crypto/evp/encode.c b/crypto/evp/encode.c index 53cc586396..91160adec0 100644 --- a/crypto/evp/encode.c +++ b/crypto/evp/encode.c @@ -57,12 +57,13 @@ */ #include -#include "cryptlib.h" +#include "internal/cryptlib.h" #include +#include "evp_locl.h" +static unsigned char conv_ascii2bin(unsigned char a); #ifndef CHARSET_EBCDIC # define conv_bin2ascii(a) (data_bin2ascii[(a)&0x3f]) -# define conv_ascii2bin(a) (data_ascii2bin[(a)&0x7f]) #else /* * We assume that PEM encoded files are EBCDIC files (i.e., printable text @@ -71,7 +72,6 @@ * as the underlying textstring data_bin2ascii[] is already EBCDIC) */ # define conv_bin2ascii(a) (data_bin2ascii[(a)&0x3f]) -# define conv_ascii2bin(a) (data_ascii2bin[os_toascii[a]&0x7f]) #endif /*- @@ -103,6 +103,7 @@ abcdefghijklmnopqrstuvwxyz0123456789+/"; #define B64_WS 0xE0 #define B64_ERROR 0xFF #define B64_NOT_BASE64(a) (((a)|0x13) == 0xF3) +#define B64_BASE64(a) !B64_NOT_BASE64(a) static const unsigned char data_ascii2bin[128] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, @@ -123,6 +124,37 @@ static const unsigned char data_ascii2bin[128] = { 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; +#ifndef CHARSET_EBCDIC +static unsigned char conv_ascii2bin(unsigned char a) +{ + if (a & 0x80) + return B64_ERROR; + return data_ascii2bin[a]; +} +#else +static unsigned char conv_ascii2bin(unsigned char a) +{ + a = os_toascii[a]; + if (a & 0x80) + return B64_ERROR; + return data_ascii2bin[a]; +} +#endif + +EVP_ENCODE_CTX *EVP_ENCODE_CTX_new(void) +{ + return (EVP_ENCODE_CTX *)OPENSSL_zalloc(sizeof(EVP_ENCODE_CTX)); +} + +void EVP_ENCODE_CTX_free(EVP_ENCODE_CTX *ctx) +{ + OPENSSL_free(ctx); +} +int EVP_ENCODE_CTX_num(EVP_ENCODE_CTX *ctx) +{ + return ctx->num; +} + void EVP_EncodeInit(EVP_ENCODE_CTX *ctx) { ctx->length = 48; @@ -137,7 +169,7 @@ void EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl, unsigned int total = 0; *outl = 0; - if (inl == 0) + if (inl <= 0) return; OPENSSL_assert(ctx->length <= (int)sizeof(ctx->enc_data)); if ((ctx->num + inl) < ctx->length) { @@ -218,8 +250,9 @@ int EVP_EncodeBlock(unsigned char *t, const unsigned char *f, int dlen) void EVP_DecodeInit(EVP_ENCODE_CTX *ctx) { - ctx->length = 30; + /* Only ctx->num is used during decoding. */ ctx->num = 0; + ctx->length = 0; ctx->line_num = 0; ctx->expect_nl = 0; } @@ -228,139 +261,123 @@ void EVP_DecodeInit(EVP_ENCODE_CTX *ctx) * -1 for error * 0 for last line * 1 for full line + * + * Note: even though EVP_DecodeUpdate attempts to detect and report end of + * content, the context doesn't currently remember it and will accept more data + * in the next call. Therefore, the caller is responsible for checking and + * rejecting a 0 return value in the middle of content. + * + * Note: even though EVP_DecodeUpdate has historically tried to detect end of + * content based on line length, this has never worked properly. Therefore, + * we now return 0 when one of the following is true: + * - Padding or B64_EOF was detected and the last block is complete. + * - Input has zero-length. + * -1 is returned if: + * - Invalid characters are detected. + * - There is extra trailing padding, or data after padding. + * - B64_EOF is detected after an incomplete base64 block. */ int EVP_DecodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl) { - int seof = -1, eof = 0, rv = -1, ret = 0, i, v, tmp, n, ln, exp_nl; + int seof = 0, eof = 0, rv = -1, ret = 0, i, v, tmp, n, decoded_len; unsigned char *d; n = ctx->num; d = ctx->enc_data; - ln = ctx->line_num; - exp_nl = ctx->expect_nl; - /* last line of input. */ - if ((inl == 0) || ((n == 0) && (conv_ascii2bin(in[0]) == B64_EOF))) { + if (n > 0 && d[n - 1] == '=') { + eof++; + if (n > 1 && d[n - 2] == '=') + eof++; + } + + /* Legacy behaviour: an empty input chunk signals end of input. */ + if (inl == 0) { rv = 0; goto end; } - /* We parse the input data */ for (i = 0; i < inl; i++) { - /* If the current line is > 80 characters, scream a lot */ - if (ln >= 80) { - rv = -1; - goto end; - } - - /* Get char and put it into the buffer */ tmp = *(in++); v = conv_ascii2bin(tmp); - /* only save the good data :-) */ - if (!B64_NOT_BASE64(v)) { - OPENSSL_assert(n < (int)sizeof(ctx->enc_data)); - d[n++] = tmp; - ln++; - } else if (v == B64_ERROR) { + if (v == B64_ERROR) { rv = -1; goto end; } - /* - * have we seen a '=' which is 'definitly' the last input line. seof - * will point to the character that holds it. and eof will hold how - * many characters to chop off. - */ if (tmp == '=') { - if (seof == -1) - seof = n; eof++; + } else if (eof > 0 && B64_BASE64(v)) { + /* More data after padding. */ + rv = -1; + goto end; } - if (v == B64_CR) { - ln = 0; - if (exp_nl) - continue; + if (eof > 2) { + rv = -1; + goto end; } - /* eoln */ - if (v == B64_EOLN) { - ln = 0; - if (exp_nl) { - exp_nl = 0; - continue; - } - } - exp_nl = 0; - - /* - * If we are at the end of input and it looks like a line, process - * it. - */ - if (((i + 1) == inl) && (((n & 3) == 0) || eof)) { - v = B64_EOF; - /* - * In case things were given us in really small records (so two - * '=' were given in separate updates), eof may contain the - * incorrect number of ending bytes to skip, so let's redo the - * count - */ - eof = 0; - if (d[n - 1] == '=') - eof++; - if (d[n - 2] == '=') - eof++; - /* There will never be more than two '=' */ + if (v == B64_EOF) { + seof = 1; + goto tail; } - if ((v == B64_EOF && (n & 3) == 0) || (n >= 64)) { - /* - * This is needed to work correctly on 64 byte input lines. We - * process the line and then need to accept the '\n' - */ - if ((v != B64_EOF) && (n >= 64)) - exp_nl = 1; - if (n > 0) { - v = EVP_DecodeBlock(out, d, n); - n = 0; - if (v < 0) { - rv = 0; - goto end; - } - if (eof > v) { - rv = -1; - goto end; - } - ret += (v - eof); - } else { - eof = 1; - v = 0; + /* Only save valid base64 characters. */ + if (B64_BASE64(v)) { + if (n >= 64) { + /* + * We increment n once per loop, and empty the buffer as soon as + * we reach 64 characters, so this can only happen if someone's + * manually messed with the ctx. Refuse to write any more data. + */ + rv = -1; + goto end; } + OPENSSL_assert(n < (int)sizeof(ctx->enc_data)); + d[n++] = tmp; + } - /* - * This is the case where we have had a short but valid input - * line - */ - if ((v < ctx->length) && eof) { - rv = 0; + if (n == 64) { + decoded_len = EVP_DecodeBlock(out, d, n); + n = 0; + if (decoded_len < 0 || eof > decoded_len) { + rv = -1; goto end; - } else - ctx->length = v; + } + ret += decoded_len - eof; + out += decoded_len - eof; + } + } - if (seof >= 0) { - rv = 0; + /* + * Legacy behaviour: if the current line is a full base64-block (i.e., has + * 0 mod 4 base64 characters), it is processed immediately. We keep this + * behaviour as applications may not be calling EVP_DecodeFinal properly. + */ +tail: + if (n > 0) { + if ((n & 3) == 0) { + decoded_len = EVP_DecodeBlock(out, d, n); + n = 0; + if (decoded_len < 0 || eof > decoded_len) { + rv = -1; goto end; } - out += v; + ret += (decoded_len - eof); + } else if (seof) { + /* EOF in the middle of a base64 block. */ + rv = -1; + goto end; } } - rv = 1; - end: + + rv = seof || (n == 0 && eof) ? 0 : 1; +end: + /* Legacy behaviour. This should probably rather be zeroed on error. */ *outl = ret; ctx->num = n; - ctx->line_num = ln; - ctx->expect_nl = exp_nl; return (rv); } @@ -418,35 +435,3 @@ int EVP_DecodeFinal(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl) } else return (1); } - -#ifdef undef -int EVP_DecodeValid(unsigned char *buf, int len) -{ - int i, num = 0, bad = 0; - - if (len == 0) - return (-1); - while (conv_ascii2bin(*buf) == B64_WS) { - buf++; - len--; - if (len == 0) - return (-1); - } - - for (i = len; i >= 4; i -= 4) { - if ((conv_ascii2bin(buf[0]) >= 0x40) || - (conv_ascii2bin(buf[1]) >= 0x40) || - (conv_ascii2bin(buf[2]) >= 0x40) || - (conv_ascii2bin(buf[3]) >= 0x40)) - return (-1); - buf += 4; - num += 1 + (buf[2] != '=') + (buf[3] != '='); - } - if ((i == 1) && (conv_ascii2bin(buf[0]) == B64_EOLN)) - return (num); - if ((i == 2) && (conv_ascii2bin(buf[0]) == B64_EOLN) && - (conv_ascii2bin(buf[0]) == B64_EOLN)) - return (num); - return (1); -} -#endif