mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
587 lines
20 KiB
C
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));
|
|
}
|
|
|