Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

587 lines
20 KiB
C

/*H*******************************************************************/
/*!
\File base64.c
\Description
This module implements Base-64 encoding/decoding as defined in RFC
4648: https://tools.ietf.org/html/rfc4648
\Notes
No strict compliance check has been made. Instead it has only
been confirmed that Decode(Encode(x)) = x for various inputs
which was all that was required for the original application.
\Copyright
Copyright (c) Electronic Arts 2003-2017. ALL RIGHTS RESERVED.
\Version 1.0 12/11/2003 (SJB) First Version
*/
/*******************************************************************H*/
/*** Include files ***************************************************/
#include "DirtySDK/platform.h"
#include "DirtySDK/util/base64.h"
/*** Defines *********************************************************/
/*** Type Definitions ************************************************/
/*** Variables *******************************************************/
/*
The table used in converting an unsigned char -> Base64.
This is as per the RFC.
*/
static const char _Base64_strEncode[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
/* The table used in converting an unsigned char -> Base64 (URL)
This is as per the RFC: https://tools.ietf.org/html/rfc4648#section-5 */
static const char _Base64_strEncodeUrl[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-_";
/*
The table used in coverting Base64 -> unsigned char.
This is not strictly needed since this mapping can be determined
by searching _Base64_Encode, but by having it as a separate table
it means the decode can run in O(n) time where n is the length of
the input rather than O(n*m) where m is 64.
The table is designed such that after subtracting '+' from the
base 64 value, the corresponding char value can be found by
indexing into this table. Since the characters chosen by the
Base64 designers are not contiguous in ASCII there are some holes
in the table and these are filled with -1 to indicate an invalid
value.
*/
static const signed char _Base64_strDecode[] =
{
62, // +
-1, -1, -1, // ,-.
63, // /
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // 0-9
-1, -1, -1, -1, -1, -1, -1, // :;<=>?@
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, // A-
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // -Z
-1, -1, -1, -1, -1, -1, // [\]^_`
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // a-
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // -z
};
static const signed char _Base64_strDecodeUrl[] =
{
-1, -1, // +,
62, // -
-1, -1, // ./
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // 0-9
-1, -1, -1, -1, -1, -1, -1, // :;<=>?@
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, // A-
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // -Z
-1, -1, -1, -1, // [\]^
63, // _
-1, // `
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // a-
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // -z
};
/*** Private functions ***********************************************/
/*F*******************************************************************/
/*!
\Function _Base64Encode
\Description
Base-64 encode a string.
\Input iInputLen - the length of the input
\Input *pInput - the input
\Input *pOutput - the output
\Input iOutputLen - the output buffer size
\Input *pEncode - the table to use for encoding
\Input bPadded - do you want padding to be encoded?
\Output
int32_t - encoded length, or -1 on error
\Notes
pOutput is NUL terminated.
It is assumed that pOutput is large enough to hold
Base64EncodedSize(iInputLen)+1 bytes.
The following chart should help explain the various bit shifts
that are in the code. On the top are the 8-bit bytes of the
input and on the bottom are the 6-bit bytes of the output :-
\verbatim
| 0 | 1 | 2 | in
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0 | 1 | 2 | 3 | out
\endverbatim
As the above shows the out0 is the top 6 bits of in0, out1 is
the bottom two bits of in0 along with the top 4 bits of in1,
... etc.
To speed things up the encoder tries to work on chunks of 3
input bytes at a time since that produces 4 output bytes
without having to maintain any inter-byte processing state.
For any input that is not a multiple of 3 then 1 or 2 padding
bytes are are added as appropriate as described in the RFC.
\Version 12/11/2002 (sbevan) First Version
*/
/*******************************************************************F*/
static int32_t _Base64Encode(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen, const char *pEncode, uint8_t bPadded)
{
int32_t iInp, iOut;
// validate output buffer size, including null termination
if ((Base64EncodedSize(iInputLen)+1) > iOutputLen)
{
return(-1);
}
// encode the data
for (iInp=0, iOut=0; iInputLen >= 3; iOut += 4, iInp += 3, iInputLen -= 3)
{
pOutput[iOut+0] = pEncode[(pInput[iInp]>>2)&0x3f];
pOutput[iOut+1] = pEncode[((pInput[iInp]&0x3)<<4)|((pInput[iInp+1]>>4)&0x0f)];
pOutput[iOut+2] = pEncode[((pInput[iInp+1]&0xf)<<2)|((pInput[iInp+2]>>6)&0x03)];
pOutput[iOut+3] = pEncode[(pInput[iInp+2]&0x3f)];
}
switch (iInputLen)
{
case 1:
pOutput[iOut+0] = pEncode[(pInput[iInp]>>2)&0x3f];
pOutput[iOut+1] = pEncode[((pInput[iInp]&0x3)<<4)];
if (bPadded)
{
pOutput[iOut+2] = '=';
pOutput[iOut+3] = '=';
iOut += 4;
}
else
{
iOut += 2;
}
break;
case 2:
pOutput[iOut+0] = pEncode[(pInput[iInp]>>2)&0x3f];
pOutput[iOut+1] = pEncode[((pInput[iInp]&0x3)<<4)|((pInput[iInp+1]>>4)&0x0f)];
pOutput[iOut+2] = pEncode[((pInput[iInp+1]&0xf)<<2)];
if (bPadded)
{
pOutput[iOut+3] = '=';
iOut += 4;
}
else
{
iOut += 3;
}
break;
}
pOutput[iOut] = '\0';
return(iOut);
}
/*F*******************************************************************/
/*!
\Function _Base64Decode
\Description
Decode a Base-64 encoded string.
\Input *pInput - the Base-64 encoded string
\Input iInputLen - the length of the encoded input
\Input *pOutput - [out] the decoded string, or NULL to calculate output size
\Input iOutputLen - size of output buffer
\Input *pDecode - the table to use for the decoding
\Output
int32_t - decoded size on success, zero on error.
\Notes
The only reasons for failure are if the input base64 sequence does
not amount to a number of characters comprising an even multiple
of four characters, or if the input contains a character not in
the Base64 character set and not a whitespace character. This
behavior is mentioned in the RFC:
"In base64 data, characters other than those in Table 1,
line breaks, and other white space probably indicate a
transmission error, about which a warning message or even
a message rejection might be appropriate under some
circumstances."
If pOutput is NULL, the function will return the number of
characters required to buffer the output, or zero on error.
The following chart should help explain the various bit shifts
that are in the code. On the top are the 6-bit bytes in the
input and and on the bottom are the 8-bit bytes of the output :-
\verbatim
| 0 | 1 | 2 | 3 | in
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0 | 1 | 2 | out
\endverbatim
As the above shows the out0 consists of all of in0 and the top
2 bits of in1, out1 is the bottom 4 bits of in1 and the top 4
bits of in2, ... etc.
\Version 12/11/2002 (sbevan) First Version
\Version 01/29/2009 (jbrookes) Enhanced to skip whitespace, added decoded length functionality
*/
/*******************************************************************F*/
static int32_t _Base64Decode(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen, const signed char *pDecode)
{
char ci[4];
int8_t co0, co1, co2, co3;
int32_t iInputCnt, iInputOff, iOutputOff;
for (iInputOff = 0, iOutputOff = 0; iInputOff < iInputLen; )
{
// scan input
for (iInputCnt = 0; (iInputCnt < 4) && (iInputOff < iInputLen) && (pInput[iInputOff] != '\0') && (iOutputOff < iOutputLen); iInputOff += 1)
{
// ignore whitespace
if ((pInput[iInputOff] == ' ') || (pInput[iInputOff] == '\t') || (pInput[iInputOff] == '\r') || (pInput[iInputOff] == '\n'))
{
continue;
}
// basic range validation
else if ((pInput[iInputOff] < '+') || (pInput[iInputOff] > 'z'))
{
return(0);
}
// fetch input character
else
{
ci[iInputCnt++] = pInput[iInputOff];
}
}
// if we didn't get anything, we're done
if (iInputCnt == 0)
{
break;
}
if (iInputCnt < 4)
{
// error if we did not get at least 2
if (iInputCnt < 2)
{
return(0);
}
// otherwise for everything under the quad, default to padding
if (iInputCnt < 3)
{
ci[2] = '=';
}
ci[3] = '=';
}
// decode the sequence
co0 = pDecode[(int32_t)ci[0]-'+'];
co1 = pDecode[(int32_t)ci[1]-'+'];
co2 = pDecode[(int32_t)ci[2]-'+'];
co3 = pDecode[(int32_t)ci[3]-'+'];
if ((co0 >= 0) && (co1 >= 0))
{
if ((co2 >= 0) && (co3 >= 0))
{
if ((pOutput != NULL) && ((iOutputLen - iOutputOff) > 2))
{
pOutput[iOutputOff+0] = (co0<<2)|((co1>>4)&0x3);
pOutput[iOutputOff+1] = (co1&0x3f)<<4|((co2>>2)&0x3F);
pOutput[iOutputOff+2] = ((co2&0x3)<<6)|co3;
}
iOutputOff += 3;
}
else if ((co2 >= 0) && (ci[3] == '='))
{
if ((pOutput != NULL) && ((iOutputLen - iOutputOff) > 1))
{
pOutput[iOutputOff+0] = (co0<<2)|((co1>>4)&0x3);
pOutput[iOutputOff+1] = (co1&0x3f)<<4|((co2>>2)&0x3F);
}
iOutputOff += 2;
// consider input complete
iInputOff = iInputLen;
}
else if ((ci[2] == '=') && (ci[3] == '='))
{
if ((pOutput != NULL) && ((iOutputLen - iOutputOff) > 0))
{
pOutput[iOutputOff+0] = (co0<<2)|((co1>>4)&0x3);
}
iOutputOff += 1;
// consider input complete
iInputOff = iInputLen;
}
else
{
// illegal input character
return(0);
}
}
else
{
// illegal input character
return(0);
}
}
// return length of decoded data or zero if decoding failed
return((iInputOff == iInputLen) ? iOutputOff : 0);
}
/*** Public functions ************************************************/
/*F*******************************************************************/
/*!
\Function Base64Encode
\Deprecated
The calling convention for Base64Encode will be replaced with
that of Base64Encode2 in a future release.
\Description
Base-64 encode a string.
\Input iInputLen - the length of the input
\Input *pInput - the input
\Input *pOutput - the output
\Notes
pOutput is NUL terminated.
It is assumed that pOutput is large enough to hold
Base64EncodedSize(iInputLen)+1 bytes.
\Version 1.0 12/11/2002 (SJB) First Version
*/
/*******************************************************************F*/
void Base64Encode(int32_t iInputLen, const char *pInput, char *pOutput)
{
Base64Encode2(pInput, iInputLen, pOutput, 0x7fffffff);
}
/*F*******************************************************************/
/*!
\Function Base64Encode2
\Description
Base-64 encode a string.
\Input iInputLen - the length of the input
\Input *pInput - the input
\Input *pOutput - the output
\Input iOutputLen - the output buffer size
\Output
int32_t - encoded length, or -1 on error
\Notes
pOutput is NUL terminated.
\Version 12/11/2002 (sbevan) First Version
*/
/*******************************************************************F*/
int32_t Base64Encode2(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen)
{
return(_Base64Encode(pInput, iInputLen, pOutput, iOutputLen, _Base64_strEncode, TRUE));
}
/*F*******************************************************************/
/*!
\Function Base64Decode
\Deprecated
The calling convention for Base64Decode will be replaced with
that of Base64Decode3 in a future release.
\Description
Decode a Base-64 encoded string.
\Input iInputLen - the length of the encoded input
\Input *pInput - the Base-64 encoded string
\Input *pOutput - [out] the decoded string, or NULL to calculate output size
\Output
int32_t - true on success, false on error.
\Notes
It is assumed that pOutput is large enough to hold
Base64DecodedSize(iInputLen) bytes. If pOutput is NULL,
the function will return the number of characters required to
buffer the output or zero on error.
\Version 12/11/2002 (sbevan) First Version
\Version 01/29/2009 (jbrookes) Enhanced to skip whitespace, added decoded length functionality
*/
/*******************************************************************F*/
int32_t Base64Decode(int32_t iInputLen, const char *pInput, char *pOutput)
{
int32_t iOutputLen = Base64Decode2(iInputLen, pInput, pOutput);
if (pOutput != NULL)
{
iOutputLen = iOutputLen ? TRUE : FALSE;
}
return(iOutputLen);
}
/*F*******************************************************************/
/*!
\Function Base64Decode2
\Deprecated
The calling convention for Base64Decode2 will be replaced with
that of Base64Decode3 in a future release.
\Description
Decode a Base-64 encoded string.
\Input iInputLen - the length of the encoded input
\Input *pInput - the Base-64 encoded string
\Input *pOutput - [out[ the decoded string, or NULL to calculate output size
\Output
int32_t - decoded size on success, zero on error.
\Notes
It is assumed that pOutput is large enough to hold
Base64DecodedSize(iInputLen) bytes. If pOutput is NULL,
the function will return the number of characters required to
buffer the output or zero on error.
\Version 12/11/2002 (sbevan) First Version
\Version 01/29/2009 (jbrookes) Enhanced to skip whitespace, added decoded length functionality
*/
/*******************************************************************F*/
int32_t Base64Decode2(int32_t iInputLen, const char *pInput, char *pOutput)
{
return(Base64Decode3(pInput, iInputLen, pOutput, 0x7fffffff));
}
/*F*******************************************************************/
/*!
\Function Base64Decode3
\Description
Decode a Base-64 encoded string.
\Input *pInput - the Base-64 encoded string
\Input iInputLen - the length of the encoded input
\Input *pOutput - [out] the decoded string, or NULL to calculate output size
\Input iOutputLen - size of output buffer
\Output
int32_t - decoded size on success, zero on error.
\Notes
If pOutput is NULL, the function will return the number of
characters required to buffer the output, or zero on error.
\Version 12/11/2002 (sbevan) First Version
\Version 01/29/2009 (jbrookes) Enhanced to skip whitespace, added decoded length functionality
*/
/*******************************************************************F*/
int32_t Base64Decode3(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen)
{
return(_Base64Decode(pInput, iInputLen, pOutput, iOutputLen, _Base64_strDecode));
}
/*F*******************************************************************/
/*!
\Function Base64EncodeUrl
\Description
Base-64 encode a string url.
\Input *pInput - the input
\Input iInputLen - the length of the input
\Input *pOutput - the output
\Input iOutputLen - the output buffer size
\Output
int32_t - encoded length, or -1 on error
\Notes
pOutput is NUL terminated.
\Version 11/15/2017 (eesponda)
*/
/*******************************************************************F*/
int32_t Base64EncodeUrl(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen)
{
return(Base64EncodeUrl2(pInput, iInputLen, pOutput, iOutputLen, TRUE));
}
/*F*******************************************************************/
/*!
\Function Base64EncodeUrl2
\Description
Base-64 encode a string url with extra options
\Input *pInput - the input
\Input iInputLen - the length of the input
\Input *pOutput - the output
\Input iOutputLen - the output buffer size
\Input bPadded - is the output padded?
\Output
int32_t - encoded length, or -1 on error
\Notes
pOutput is NUL terminated.
\Version 05/20/2020 (eesponda)
*/
/*******************************************************************F*/
int32_t Base64EncodeUrl2(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen, uint8_t bPadded)
{
return(_Base64Encode(pInput, iInputLen, pOutput, iOutputLen, _Base64_strEncodeUrl, bPadded));
}
/*F*******************************************************************/
/*!
\Function Base64DecodeUrl
\Description
Decode a Base-64 encoded url string.
\Input *pInput - the Base-64 encoded string
\Input iInputLen - the length of the encoded input
\Input *pOutput - [out] the decoded string, or NULL to calculate output size
\Input iOutputLen - size of output buffer
\Output
int32_t - decoded size on success, zero on error.
\Notes
If pOutput is NULL, the function will return the number of
characters required to buffer the output, or zero on error.
\Version 11/15/2017 (eesponda)
*/
/*******************************************************************F*/
int32_t Base64DecodeUrl(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen)
{
return(_Base64Decode(pInput, iInputLen, pOutput, iOutputLen, _Base64_strDecodeUrl));
}