libavutil/base64.c
558b86a5
 /*
  * Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com)
  *
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  *
  * FFmpeg is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
 /**
ba87f080
  * @file
bfe3676f
  * @brief Base64 encode/decode
558b86a5
  * @author Ryan Martell <rdm4@martellventures.com> (with lots of Michael)
  */
 
 #include "common.h"
 #include "base64.h"
112bca91
 #include "intreadwrite.h"
965fa6b0
 #include "timer.h"
558b86a5
 
 /* ---------------- private code */
523f676b
 static const uint8_t map2[256] =
558b86a5
 {
523f676b
     0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff,
 
558b86a5
     0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36,
     0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff,
523f676b
     0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x01,
558b86a5
     0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
     0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
     0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b,
     0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
     0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
523f676b
     0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33,
 
                       0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
558b86a5
 };
 
420719e1
 #define BASE64_DEC_STEP(i) do { \
def19c90
     bits = map2[in[i]]; \
     if (bits & 0x80) \
daa47fdd
         goto out ## i; \
     v = i ? (v << 6) + bits : bits; \
420719e1
 } while(0)
def19c90
 
523f676b
 int av_base64_decode(uint8_t *out, const char *in_str, int out_size)
558b86a5
 {
     uint8_t *dst = out;
6af4c981
     uint8_t *end = out + out_size;
523f676b
     // no sign extension
     const uint8_t *in = in_str;
def19c90
     unsigned bits = 0xff;
daa47fdd
     unsigned v;
558b86a5
 
daa47fdd
     while (end - dst > 3) {
420719e1
         BASE64_DEC_STEP(0);
         BASE64_DEC_STEP(1);
         BASE64_DEC_STEP(2);
         BASE64_DEC_STEP(3);
daa47fdd
         // Using AV_WB32 directly confuses compiler
2bb7396b
         v = av_be2ne32(v << 8);
daa47fdd
         AV_WN32(dst, v);
         dst += 3;
def19c90
         in += 4;
     }
     if (end - dst) {
420719e1
         BASE64_DEC_STEP(0);
         BASE64_DEC_STEP(1);
daa47fdd
         BASE64_DEC_STEP(2);
         BASE64_DEC_STEP(3);
         *dst++ = v >> 16;
         if (end - dst)
             *dst++ = v >> 8;
         if (end - dst)
             *dst++ = v;
         in += 4;
def19c90
     }
     while (1) {
420719e1
         BASE64_DEC_STEP(0);
def19c90
         in++;
420719e1
         BASE64_DEC_STEP(0);
def19c90
         in++;
420719e1
         BASE64_DEC_STEP(0);
def19c90
         in++;
420719e1
         BASE64_DEC_STEP(0);
def19c90
         in++;
558b86a5
     }
 
daa47fdd
 out3:
     *dst++ = v >> 10;
     v <<= 2;
 out2:
     *dst++ = v >> 4;
 out1:
 out0:
305180f5
     return bits & 1 ? AVERROR_INVALIDDATA : dst - out;
558b86a5
 }
 
 /*****************************************************************************
bfe3676f
 * b64_encode: Stolen from VLC's http.c.
 * Simplified by Michael.
 * Fixed edge cases and made it work from data (vs. strings) by Ryan.
558b86a5
 *****************************************************************************/
 
5118bd44
 char *av_base64_encode(char *out, int out_size, const uint8_t *in, int in_size)
558b86a5
 {
     static const char b64[] =
         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
     char *ret, *dst;
     unsigned i_bits = 0;
     int i_shift = 0;
5118bd44
     int bytes_remaining = in_size;
558b86a5
 
5118bd44
     if (in_size >= UINT_MAX / 4 ||
784824a6
         out_size < AV_BASE64_SIZE(in_size))
558b86a5
         return NULL;
52ef50a0
     ret = dst = out;
112bca91
     while (bytes_remaining > 3) {
         i_bits = AV_RB32(in);
         in += 3; bytes_remaining -= 3;
         *dst++ = b64[ i_bits>>26        ];
         *dst++ = b64[(i_bits>>20) & 0x3F];
         *dst++ = b64[(i_bits>>14) & 0x3F];
         *dst++ = b64[(i_bits>>8 ) & 0x3F];
     }
     i_bits = 0;
353fa898
     while (bytes_remaining) {
52ef50a0
         i_bits = (i_bits << 8) + *in++;
353fa898
         bytes_remaining--;
         i_shift += 8;
     }
ea4da948
     while (i_shift > 0) {
d60d718c
         *dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f];
ea4da948
         i_shift -= 6;
     }
353fa898
     while ((dst - ret) & 3)
         *dst++ = '=';
558b86a5
     *dst = '\0';
 
     return ret;
 }
21eff9ce
 
 #ifdef TEST
d086c120
 // LCOV_EXCL_START
21eff9ce
 
 #define MAX_DATA_SIZE    1024
 #define MAX_ENCODED_SIZE 2048
 
504ffed1
 static int test_encode_decode(const uint8_t *data, unsigned int data_size,
                               const char *encoded_ref)
21eff9ce
 {
     char  encoded[MAX_ENCODED_SIZE];
     uint8_t data2[MAX_DATA_SIZE];
     int data2_size, max_data2_size = MAX_DATA_SIZE;
 
     if (!av_base64_encode(encoded, MAX_ENCODED_SIZE, data, data_size)) {
         printf("Failed: cannot encode the input data\n");
         return 1;
     }
     if (encoded_ref && strcmp(encoded, encoded_ref)) {
         printf("Failed: encoded string differs from reference\n"
                "Encoded:\n%s\nReference:\n%s\n", encoded, encoded_ref);
         return 1;
     }
 
8650d5fa
     if ((data2_size = av_base64_decode(data2, encoded, max_data2_size)) != data_size) {
21eff9ce
         printf("Failed: cannot decode the encoded string\n"
                "Encoded:\n%s\n", encoded);
         return 1;
     }
8650d5fa
     if ((data2_size = av_base64_decode(data2, encoded, data_size)) != data_size) {
         printf("Failed: cannot decode with minimal buffer\n"
                "Encoded:\n%s\n", encoded);
         return 1;
     }
21eff9ce
     if (memcmp(data2, data, data_size)) {
         printf("Failed: encoded/decoded data differs from original data\n");
         return 1;
     }
8650d5fa
     if (av_base64_decode(NULL, encoded, 0) != 0) {
         printf("Failed: decode to NULL buffer\n");
         return 1;
     }
     if (strlen(encoded)) {
         char *end = strchr(encoded, '=');
         if (!end)
             end = encoded + strlen(encoded) - 1;
         *end = '%';
         if (av_base64_decode(NULL, encoded, 0) >= 0) {
             printf("Failed: error detection\n");
             return 1;
         }
     }
21eff9ce
 
     printf("Passed!\n");
     return 0;
 }
 
bdd739e9
 int main(int argc, char ** argv)
21eff9ce
 {
     int i, error_count = 0;
     struct test {
         const uint8_t *data;
         const char *encoded_ref;
     } tests[] = {
         { "",        ""},
         { "1",       "MQ=="},
         { "22",      "MjI="},
         { "333",     "MzMz"},
         { "4444",    "NDQ0NA=="},
         { "55555",   "NTU1NTU="},
         { "666666",  "NjY2NjY2"},
         { "abc:def", "YWJjOmRlZg=="},
     };
a41687c2
     char in[1024], out[2048];
21eff9ce
 
     printf("Encoding/decoding tests\n");
     for (i = 0; i < FF_ARRAY_ELEMS(tests); i++)
         error_count += test_encode_decode(tests[i].data, strlen(tests[i].data), tests[i].encoded_ref);
 
bdd739e9
     if (argc>1 && !strcmp(argv[1], "-t")) {
         memset(in, 123, sizeof(in));
         for(i=0; i<10000; i++){
             START_TIMER
             av_base64_encode(out, sizeof(out), in, sizeof(in));
             STOP_TIMER("encode")
         }
         for(i=0; i<10000; i++){
             START_TIMER
             av_base64_decode(in, out, sizeof(in));
             STOP_TIMER("decode")
         }
a41687c2
 
bdd739e9
         for(i=0; i<10000; i++){
             START_TIMER
             av_base64_decode(NULL, out, 0);
             STOP_TIMER("syntax check")
         }
01084336
     }
 
cbbd0ac7
     if (error_count)
         printf("Error Count: %d.\n", error_count);
 
     return !!error_count;
21eff9ce
 }
 
d086c120
 // LCOV_EXCL_STOP
21eff9ce
 #endif