379 lines
12 KiB
C++
379 lines
12 KiB
C++
|
|
#include "precomp.h"
|
||
|
|
|
||
|
|
#include <cstdlib>
|
||
|
|
#include <string>
|
||
|
|
#include <cassert>
|
||
|
|
|
||
|
|
#include "base64.h"
|
||
|
|
|
||
|
|
using namespace std;
|
||
|
|
|
||
|
|
/*
|
||
|
|
* The Base 64 Alphabet
|
||
|
|
*
|
||
|
|
* Value Encoding Value Encoding Value Encoding Value Encoding
|
||
|
|
* 0 A 17 R 34 i 51 z
|
||
|
|
* 1 B 18 S 35 j 52 0
|
||
|
|
* 2 C 19 T 36 k 53 1
|
||
|
|
* 3 D 20 U 37 l 54 2
|
||
|
|
* 4 E 21 V 38 m 55 3
|
||
|
|
* 5 F 22 W 39 n 56 4
|
||
|
|
* 6 G 23 X 40 o 57 5
|
||
|
|
* 7 H 24 Y 41 p 58 6
|
||
|
|
* 8 I 25 Z 42 q 59 7
|
||
|
|
* 9 J 26 a 43 r 60 8
|
||
|
|
* 10 K 27 b 44 s 61 9
|
||
|
|
* 11 L 28 c 45 t 62 +
|
||
|
|
* 12 M 29 d 46 u 63 /
|
||
|
|
* 13 N 30 e 47 v
|
||
|
|
* 14 O 31 f 48 w (pad) =
|
||
|
|
* 15 P 32 g 49 x
|
||
|
|
* 16 Q 33 h 50 y
|
||
|
|
*/
|
||
|
|
|
||
|
|
const char BASE64::alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||
|
|
|
||
|
|
const char BASE64::values[] = {
|
||
|
|
/* 0 1 2 3 4 5 6 7 8 9 */ /* 0123456789 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 00 -> 09 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 10 -> 19 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 20 -> 29 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 30 -> 39 */
|
||
|
|
|
||
|
|
-1,-1,-1,62,-1,-1,-1,63, /* ()*+'-./ - 40 -> 47 */
|
||
|
|
52,53,54,55,56,57,58,59,60,61, /* 0123456789 - 48 -> 57 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1, 0, 1, 2, /* :;<=>?@ABC - 58 -> 67 */
|
||
|
|
3, 4, 5, 6, 7, 8, 9,10,11,12, /* DEFGHIJKLM - 68 -> 77 */
|
||
|
|
13,14,15,16,17,18,19,20,21,22, /* NOPQRSTUVW - 78 -> 87 */
|
||
|
|
23,24,25,-1,-1,-1,-1,-1,-1,26, /* XYZ[\]^_`a - 88 -> 97 */
|
||
|
|
27,28,29,30,31,32,33,34,35,36, /* bcdefghijk - 98 -> 107 */
|
||
|
|
37,38,39,40,41,42,43,44,45,46, /* lmnopqrstu - 108 -> 117 */
|
||
|
|
47,48,49,50,51, /* vwxyz - 118 -> 122 */
|
||
|
|
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1, /* - 123 -> 130 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 131 -> 140 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 141 -> 150 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 151 -> 160 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 161 -> 170 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 171 -> 180 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 181 -> 190 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 191 -> 200 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 201 -> 210 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 211 -> 220 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 221 -> 230 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 231 -> 240 */
|
||
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* - 241 -> 250 */
|
||
|
|
-1,-1,-1,-1,-1, /* - 251 -> 255 */
|
||
|
|
};
|
||
|
|
|
||
|
|
BASE64::BASE64()
|
||
|
|
{
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
BASE64::~BASE64()
|
||
|
|
{
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
string BASE64::encode(unsigned char * buf, int len, bool padding)
|
||
|
|
{
|
||
|
|
int pad;
|
||
|
|
int enclen = encode_pad_length(len, &pad);
|
||
|
|
string encbuf;
|
||
|
|
|
||
|
|
if (!padding)
|
||
|
|
pad = 0;
|
||
|
|
|
||
|
|
encbuf.resize(enclen + pad); /* Allow for trailing NUL */
|
||
|
|
|
||
|
|
encode_exactly(buf, len, encbuf.data(), enclen);
|
||
|
|
|
||
|
|
if (pad)
|
||
|
|
memset(encbuf.data() + enclen, '=', pad);
|
||
|
|
|
||
|
|
return encbuf;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* encode_exactly
|
||
|
|
*
|
||
|
|
* Encode `len' bytes from `buf' unsignedo `enclen' bytes starting from `encbuf'.
|
||
|
|
* Caller must have ensured that there was EXACTLY the needed room in encbuf.
|
||
|
|
*/
|
||
|
|
void BASE64::encode_exactly(const unsigned char *buf, int len, char *encbuf, int enclen)
|
||
|
|
{
|
||
|
|
int i = 0; /* Input accumulator, 0 for trailing pad */
|
||
|
|
unsigned char const *ip = buf + len; /* Input pounsigneder, one byte off end */
|
||
|
|
char *op = encbuf + enclen; /* Output pounsigneder, one byte off end */
|
||
|
|
|
||
|
|
assert(buf);
|
||
|
|
assert(encbuf);
|
||
|
|
assert(len > 0);
|
||
|
|
assert(enclen >= len * 4 / 3);
|
||
|
|
|
||
|
|
/*
|
||
|
|
* In the following picture, we represent how the 3 bytes of input
|
||
|
|
* are split unsignedo groups of 6 bits, each group being encoded as a
|
||
|
|
* single base64 digit.
|
||
|
|
*
|
||
|
|
* input byte 0 1 2
|
||
|
|
* +--------+--------+--------+
|
||
|
|
* |01234501|23450123|45012345|
|
||
|
|
* +--------+--------+--------+
|
||
|
|
* <----><-----><-----><---->
|
||
|
|
* output digit 0 1 2 3
|
||
|
|
*
|
||
|
|
*
|
||
|
|
* Because of possible padding, which must be done as if the input
|
||
|
|
* was 0, and because the fractional part is at the end, we'll
|
||
|
|
* start encoding from the end. The encoding loop is unrolled for
|
||
|
|
* greater performance (using the infamous Duff's device to directly
|
||
|
|
* switch at the proper stage within the do {} while loop).
|
||
|
|
*/
|
||
|
|
|
||
|
|
switch (len % 3) {
|
||
|
|
case 0:
|
||
|
|
do {
|
||
|
|
assert(op - encbuf >= 4);
|
||
|
|
i = (unsigned char) *--ip; /* Input #2 */
|
||
|
|
*--op = alphabet[i & 0x3f]; /* Ouput #3 */
|
||
|
|
i >>= 6; /* upper <45>, input #2 */
|
||
|
|
/* FALLTHROUGH */
|
||
|
|
case 2:
|
||
|
|
i |= ((unsigned char) *--ip) << 2; /* had 2 bits in `i' */
|
||
|
|
*--op = alphabet[i & 0x3f]; /* Output #2 */
|
||
|
|
i >>= 6; /* upper <2345>, input #1 */
|
||
|
|
/* FALLTHROUGH */
|
||
|
|
case 1:
|
||
|
|
i |= ((unsigned char) *--ip) << 4; /* had 4 bits in `i' */
|
||
|
|
*--op = alphabet[i & 0x3f]; /* Output #1 */
|
||
|
|
i >>= 6; /* upper <012345>, input #0 */
|
||
|
|
*--op = alphabet[i & 0x3f]; /* Output #0 */
|
||
|
|
i >>= 6; /* Holds nothing, MBZ */
|
||
|
|
assert(i == 0);
|
||
|
|
assert(op >= encbuf);
|
||
|
|
} while (op > encbuf);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* encode_pad_length
|
||
|
|
*
|
||
|
|
* Compute the number of base64 digits and amount of padding necessary
|
||
|
|
* to encode `len' bytes.
|
||
|
|
*
|
||
|
|
* Returns the number of base64 digits necessary.
|
||
|
|
* Furthermore, if `pad' is a non-NULL pounsigneder, it is filled with the amount
|
||
|
|
* of padding chars that would be necessary.
|
||
|
|
*/
|
||
|
|
int BASE64::encode_pad_length(int len, int *pad)
|
||
|
|
{
|
||
|
|
int ndigits; /* Base64 digits necessary */
|
||
|
|
int npad = 0; /* Final padding chars necessary */
|
||
|
|
int tcount; /* Amount of full triplets */
|
||
|
|
int remainder; /* Amount of input bytes in final triplet */
|
||
|
|
|
||
|
|
assert(len > 0);
|
||
|
|
|
||
|
|
tcount = len / 3;
|
||
|
|
remainder = len - (tcount * 3);
|
||
|
|
|
||
|
|
assert((unsigned) remainder >= 0);
|
||
|
|
|
||
|
|
switch (remainder) {
|
||
|
|
case 0: npad = 0; break;
|
||
|
|
case 1: npad = 2; break;
|
||
|
|
case 2: npad = 1; break;
|
||
|
|
default: assert(0); /* Not possible */
|
||
|
|
}
|
||
|
|
|
||
|
|
ndigits = tcount * 4; /* Each full triplet encoded on 4 bytes */
|
||
|
|
if (npad != 0)
|
||
|
|
ndigits += (4 - npad);
|
||
|
|
|
||
|
|
if (pad)
|
||
|
|
*pad = npad;
|
||
|
|
|
||
|
|
return ndigits;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* base64_decode
|
||
|
|
*
|
||
|
|
* Decode `len' bytes starting at `buf' unsignedo new allocated buffer.
|
||
|
|
*
|
||
|
|
* Returns the new decoded buffer, or NULL if the input was not valid base64
|
||
|
|
* encoding. The caller knows the length of the returned buffer: it's the
|
||
|
|
* size of the input divided by 4 and multiplied by 3. If `outlen' is non-NULL,
|
||
|
|
* it is filled with the amount of bytes decoded unsignedo the buffer (without
|
||
|
|
* trailing padding).
|
||
|
|
*/
|
||
|
|
vector<unsigned char> BASE64::decode(const char * buf, int len, int * outlen)
|
||
|
|
{
|
||
|
|
int declen;
|
||
|
|
vector<unsigned char> decbuf;
|
||
|
|
int decoded;
|
||
|
|
|
||
|
|
if (len == -1)
|
||
|
|
len = strlen(buf);
|
||
|
|
|
||
|
|
// if (len == 0 || (len & 0x3)) /* Empty, or padding bytes missing */
|
||
|
|
// return NULL;
|
||
|
|
|
||
|
|
declen = (len >> 2) * 3;
|
||
|
|
decbuf.resize(declen * sizeof(unsigned char));
|
||
|
|
|
||
|
|
decoded = decode_into(buf, len, decbuf.data(), declen);
|
||
|
|
|
||
|
|
if (decoded == 0)
|
||
|
|
decbuf.clear();
|
||
|
|
|
||
|
|
if (outlen)
|
||
|
|
*outlen = decoded;
|
||
|
|
|
||
|
|
return decbuf;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* decode_into
|
||
|
|
*
|
||
|
|
* Decode `len' bytes from `buf' unsignedo `declen' bytes starting from `decbuf',
|
||
|
|
* faking the necessary amount of padding if necessary.
|
||
|
|
* Caller must have ensured that there was sufficient room in decbuf.
|
||
|
|
*
|
||
|
|
* Returns the amount of bytes decoded (without trailing padding) if successful,
|
||
|
|
* 0 if the input was not valid base32.
|
||
|
|
*/
|
||
|
|
int BASE64::decode_into(const char *buf, int len, unsigned char *decbuf, int declen)
|
||
|
|
{
|
||
|
|
int padding = 0;
|
||
|
|
|
||
|
|
if (len & 0x3)
|
||
|
|
padding = 4 - (len & 0x3);
|
||
|
|
|
||
|
|
return decode_alphabet(values, buf, len + padding, decbuf, declen, padding);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* decode_alphabet
|
||
|
|
*
|
||
|
|
* Decode `len' bytes from `buf' unsignedo `declen' bytes starting from `decbuf'.
|
||
|
|
* Caller must have ensured that there was sufficient room in decbuf.
|
||
|
|
* Uses the specified decoding alphabet.
|
||
|
|
*
|
||
|
|
* `padding', when non-zero, is the amount of padding that is missing from
|
||
|
|
* the input buffer and which we must assume.
|
||
|
|
*
|
||
|
|
* Return decoded bytes if successful, 0 if the input was not valid base32.
|
||
|
|
*/
|
||
|
|
int BASE64::decode_alphabet(const char valmap[],
|
||
|
|
const char *buf, int len, unsigned char *decbuf, int declen, int padding)
|
||
|
|
{
|
||
|
|
int i = 0; /* Input accumulator, 0 for trailing pad */
|
||
|
|
char const *ip = buf + len; /* Input pounsigneder, one byte off end */
|
||
|
|
int dlen = (len >> 2) * 3; /* Exact decoded length */
|
||
|
|
unsigned char *op; /* Output pounsigneder, one byte off end */
|
||
|
|
int bytes; /* Bytes decoded without padding */
|
||
|
|
char v;
|
||
|
|
|
||
|
|
assert(buf);
|
||
|
|
assert(decbuf);
|
||
|
|
assert(len > 0);
|
||
|
|
assert((len & 0x3) == 0); /* `len' is a multiple of 4 bytes */
|
||
|
|
//assert(declen >= dlen);
|
||
|
|
|
||
|
|
/*
|
||
|
|
* If the last byte of input is '=', there is padding and we need to
|
||
|
|
* zero the tail of the decoding buffer.
|
||
|
|
*
|
||
|
|
* Likewise, when `padding' is non-zero, act as if the '=' were there.
|
||
|
|
*/
|
||
|
|
|
||
|
|
if (buf[len-1] == '=' || padding > 0) {
|
||
|
|
int pad = 0;
|
||
|
|
int n = 0; /* Amount of bytes to zero */
|
||
|
|
int s = 0; /* Amount of bytes to zero */
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Remove and count trailing input padding bytes.
|
||
|
|
*/
|
||
|
|
|
||
|
|
if (padding == 0) {
|
||
|
|
while (*--ip == '=')
|
||
|
|
pad++;
|
||
|
|
ip++; /* Pounsigneds one byte after real non-padding input */
|
||
|
|
} else {
|
||
|
|
pad = padding;
|
||
|
|
ip -= padding;
|
||
|
|
}
|
||
|
|
|
||
|
|
switch (pad) {
|
||
|
|
case 1: n = 1; s = 0; break;
|
||
|
|
case 2: n = 2; s = 1; break;
|
||
|
|
default:
|
||
|
|
return 0; /* Cannot be valid base64 */
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(decbuf + (dlen - n), 0, n);
|
||
|
|
op = decbuf + (dlen - s);
|
||
|
|
bytes = dlen - n;
|
||
|
|
} else {
|
||
|
|
op = decbuf + dlen;
|
||
|
|
bytes = dlen;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* In the following picture, we represent how the 4 bytes of input,
|
||
|
|
* each consisting of only 6 bits of information forming a base64 digit,
|
||
|
|
* are concatenated back unsignedo 3 bytes of binary information.
|
||
|
|
*
|
||
|
|
* input digit 0 1 2 3
|
||
|
|
* <----><-----><-----><---->
|
||
|
|
* +--------+--------+--------+
|
||
|
|
* |01234501|23450123|45012345|
|
||
|
|
* +--------+--------+--------+
|
||
|
|
* output byte 0 1 2
|
||
|
|
*
|
||
|
|
* Because of possible padding, which must be done as if the input
|
||
|
|
* was 0, and because the fractional part is at the end, we'll
|
||
|
|
* start decoding from the end. The decoding loop is unrolled for
|
||
|
|
* greater performance (using the infamous Duff's device to directly
|
||
|
|
* switch at the proper stage within the do {} while loop).
|
||
|
|
*/
|
||
|
|
|
||
|
|
switch ((ip - buf) % 4) {
|
||
|
|
case 0:
|
||
|
|
do {
|
||
|
|
v = valmap[(unsigned char) *--ip]; /* Input #3 */
|
||
|
|
if (v < 0) return 0;
|
||
|
|
i = v;
|
||
|
|
/* FALLTHROUGH */
|
||
|
|
case 3:
|
||
|
|
v = valmap[(unsigned char) *--ip]; /* Input #2 */
|
||
|
|
if (v < 0) return 0;
|
||
|
|
i |= v << 6; /* had 6 bits */
|
||
|
|
*--op = i & 0xff; /* Output #2 */
|
||
|
|
i >>= 8; /* lower <0123> of output #1 */
|
||
|
|
/* FALLTHROUGH */
|
||
|
|
case 2:
|
||
|
|
v = valmap[(unsigned char) *--ip]; /* Input #1 */
|
||
|
|
if (v < 0) return 0;
|
||
|
|
i |= v << 4; /* had 4 bits */
|
||
|
|
*--op = i & 0xff; /* Output #1 */
|
||
|
|
i >>= 8; /* lower <01> of output #0 */
|
||
|
|
/* FALLTHROUGH */
|
||
|
|
case 1:
|
||
|
|
v = valmap[(unsigned char) *--ip]; /* Input #0 */
|
||
|
|
if (v < 0) return 0;
|
||
|
|
i |= v << 2; /* had 2 bits */
|
||
|
|
*--op = i & 0xff; /* Output #0 */
|
||
|
|
i >>= 8; /* Holds nothing, MBZ */
|
||
|
|
assert(i == 0);
|
||
|
|
assert(op >= decbuf);
|
||
|
|
} while (op > decbuf);
|
||
|
|
}
|
||
|
|
|
||
|
|
return bytes;
|
||
|
|
}
|