mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 10:08:18 +00:00
Add functions to encode/decode base64
This commit is contained in:
parent
32340e3cfd
commit
52d0dc8b5b
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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";
|
||||
|
|
Loading…
Reference in a new issue