Add functions to encode/decode base64

This commit is contained in:
heinrich5991 2021-07-16 02:55:27 +02:00
parent 32340e3cfd
commit 52d0dc8b5b
3 changed files with 259 additions and 0 deletions

View file

@ -3012,6 +3012,152 @@ int str_hex_decode(void *dst, int dst_size, const char *src)
return 0;
}
void str_base64(char *dst, int dst_size, const void *data_raw, int data_size)
{
static const char DIGITS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const unsigned char *data = (const unsigned char *)data_raw;
unsigned value = 0;
int num_bits = 0;
int i = 0;
int o = 0;
dst_size -= 1;
dst[dst_size] = 0;
while(true)
{
if(num_bits < 6 && i < data_size)
{
value = (value << 8) | data[i];
num_bits += 8;
i += 1;
}
if(o == dst_size)
{
return;
}
if(num_bits > 0)
{
unsigned padded;
if(num_bits >= 6)
{
padded = (value >> (num_bits - 6)) & 0x3f;
}
else
{
padded = (value << (6 - num_bits)) & 0x3f;
}
dst[o] = DIGITS[padded];
num_bits -= 6;
o += 1;
}
else if(o % 4 != 0)
{
dst[o] = '=';
o += 1;
}
else
{
dst[o] = 0;
return;
}
}
}
static int base64_digit_value(char digit)
{
if('A' <= digit && digit <= 'Z')
{
return digit - 'A';
}
else if('a' <= digit && digit <= 'z')
{
return digit - 'a' + 26;
}
else if('0' <= digit && digit <= '9')
{
return digit - '0' + 52;
}
else if(digit == '+')
{
return 62;
}
else if(digit == '/')
{
return 63;
}
return -1;
}
int str_base64_decode(void *dst_raw, int dst_size, const char *data)
{
unsigned char *dst = (unsigned char *)dst_raw;
int data_len = str_length(data);
int i;
int o = 0;
if(data_len % 4 != 0)
{
return -3;
}
if(data_len / 4 * 3 > dst_size)
{
// Output buffer too small.
return -2;
}
for(i = 0; i < data_len; i += 4)
{
int num_output_bytes = 3;
char copy[4];
int d[4];
int value;
int b;
mem_copy(copy, data + i, sizeof(copy));
if(i == data_len - 4)
{
if(copy[3] == '=')
{
copy[3] = 'A';
num_output_bytes = 2;
if(copy[2] == '=')
{
copy[2] = 'A';
num_output_bytes = 1;
}
}
}
d[0] = base64_digit_value(copy[0]);
d[1] = base64_digit_value(copy[1]);
d[2] = base64_digit_value(copy[2]);
d[3] = base64_digit_value(copy[3]);
if(d[0] == -1 || d[1] == -1 || d[2] == -1 || d[3] == -1)
{
// Invalid digit.
return -1;
}
value = (d[0] << 18) | (d[1] << 12) | (d[2] << 6) | d[3];
for(b = 0; b < 3; b++)
{
unsigned char byte_value = (value >> (16 - 8 * b)) & 0xff;
if(b < num_output_bytes)
{
dst[o] = byte_value;
o += 1;
}
else
{
if(byte_value != 0)
{
// Padding not zeroed.
return -2;
}
}
}
}
return o;
}
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"

View file

@ -1592,6 +1592,41 @@ void str_hex(char *dst, int dst_size, const void *data, int data_size);
- The contents of the buffer is only valid on success
*/
int str_hex_decode(void *dst, int dst_size, const char *src);
/*
Function: str_base64
Takes a datablock and generates the base64 encoding of it.
Parameters:
dst - Buffer to fill with base64 data
dst_size - Size of the buffer
data - Data to turn into base64
data - Size of the data
Remarks:
- The destination buffer will be zero-terminated
*/
void str_base64(char *dst, int dst_size, const void *data, int data_size);
/*
Function: str_base64_decode
Takes a base64 string without any whitespace and correct
padding and returns a byte array.
Parameters:
dst - Buffer for the byte array
dst_size - Size of the buffer
data - String to decode
Returns:
<0 - Error
>= 0 - Success, length of the resulting byte buffer
Remarks:
- The contents of the buffer is only valid on success
*/
int str_base64_decode(void *dst, int dst_size, const char *data);
/*
Function: str_timestamp
Copies a time stamp in the format year-month-day_hour-minute-second to the string.

View file

@ -221,6 +221,84 @@ TEST(Str, HexDecode)
EXPECT_STREQ(aOut, "ABCD");
}
void StrBase64Str(char *pBuffer, int BufferSize, const char *pString)
{
str_base64(pBuffer, BufferSize, pString, str_length(pString));
}
TEST(Str, Base64)
{
char aBuf[128];
str_base64(aBuf, sizeof(aBuf), "\0", 1);
EXPECT_STREQ(aBuf, "AA==");
str_base64(aBuf, sizeof(aBuf), "\0\0", 2);
EXPECT_STREQ(aBuf, "AAA=");
str_base64(aBuf, sizeof(aBuf), "\0\0\0", 3);
EXPECT_STREQ(aBuf, "AAAA");
StrBase64Str(aBuf, sizeof(aBuf), "");
EXPECT_STREQ(aBuf, "");
// https://en.wikipedia.org/w/index.php?title=Base64&oldid=1033503483#Output_padding
StrBase64Str(aBuf, sizeof(aBuf), "pleasure.");
EXPECT_STREQ(aBuf, "cGxlYXN1cmUu");
StrBase64Str(aBuf, sizeof(aBuf), "leasure.");
EXPECT_STREQ(aBuf, "bGVhc3VyZS4=");
StrBase64Str(aBuf, sizeof(aBuf), "easure.");
EXPECT_STREQ(aBuf, "ZWFzdXJlLg==");
StrBase64Str(aBuf, sizeof(aBuf), "asure.");
EXPECT_STREQ(aBuf, "YXN1cmUu");
StrBase64Str(aBuf, sizeof(aBuf), "sure.");
EXPECT_STREQ(aBuf, "c3VyZS4=");
}
TEST(Str, Base64Decode)
{
char aOut[17];
str_copy(aOut, "XXXXXXXXXXXXXXXX", sizeof(aOut));
EXPECT_EQ(str_base64_decode(aOut, sizeof(aOut), ""), 0);
EXPECT_STREQ(aOut, "XXXXXXXXXXXXXXXX");
// https://en.wikipedia.org/w/index.php?title=Base64&oldid=1033503483#Output_padding
str_copy(aOut, "XXXXXXXXXXXXXXXX", sizeof(aOut));
EXPECT_EQ(str_base64_decode(aOut, sizeof(aOut), "cGxlYXN1cmUu"), 9);
EXPECT_STREQ(aOut, "pleasure.XXXXXXX");
str_copy(aOut, "XXXXXXXXXXXXXXXX", sizeof(aOut));
EXPECT_EQ(str_base64_decode(aOut, sizeof(aOut), "bGVhc3VyZS4="), 8);
EXPECT_STREQ(aOut, "leasure.XXXXXXXX");
str_copy(aOut, "XXXXXXXXXXXXXXXX", sizeof(aOut));
EXPECT_EQ(str_base64_decode(aOut, sizeof(aOut), "ZWFzdXJlLg=="), 7);
EXPECT_STREQ(aOut, "easure.XXXXXXXXX");
str_copy(aOut, "XXXXXXXXXXXXXXXX", sizeof(aOut));
EXPECT_EQ(str_base64_decode(aOut, sizeof(aOut), "YXN1cmUu"), 6);
EXPECT_STREQ(aOut, "asure.XXXXXXXXXX");
str_copy(aOut, "XXXXXXXXXXXXXXXX", sizeof(aOut));
EXPECT_EQ(str_base64_decode(aOut, sizeof(aOut), "c3VyZS4="), 5);
EXPECT_STREQ(aOut, "sure.XXXXXXXXXXX");
str_copy(aOut, "XXXXXXXXXXXXXXXX", sizeof(aOut));
EXPECT_EQ(str_base64_decode(aOut, sizeof(aOut), "////"), 3);
EXPECT_STREQ(aOut, "\xff\xff\xffXXXXXXXXXXXXX");
}
TEST(Str, Base64DecodeError)
{
char aBuf[128];
// Wrong padding.
EXPECT_LT(str_base64_decode(aBuf, sizeof(aBuf), "A"), 0);
EXPECT_LT(str_base64_decode(aBuf, sizeof(aBuf), "AA"), 0);
EXPECT_LT(str_base64_decode(aBuf, sizeof(aBuf), "AAA"), 0);
EXPECT_LT(str_base64_decode(aBuf, sizeof(aBuf), "A==="), 0);
EXPECT_LT(str_base64_decode(aBuf, sizeof(aBuf), "=AAA"), 0);
EXPECT_LT(str_base64_decode(aBuf, sizeof(aBuf), "===="), 0);
EXPECT_LT(str_base64_decode(aBuf, sizeof(aBuf), "AAA=AAAA"), 0);
// Invalid characters.
EXPECT_LT(str_base64_decode(aBuf, sizeof(aBuf), "----"), 0);
EXPECT_LT(str_base64_decode(aBuf, sizeof(aBuf), "AAAA "), 0);
EXPECT_LT(str_base64_decode(aBuf, sizeof(aBuf), "AAA "), 0);
// Invalid padding values.
EXPECT_LT(str_base64_decode(aBuf, sizeof(aBuf), "//=="), 0);
}
TEST(Str, Tokenize)
{
char aTest[] = "GER,RUS,ZAF,BRA,CAN";