mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-01-18 04:58:51 +02:00
Add base64url encoding.
For now only encoding is supported. Decoding is not needed and may never be.
This commit is contained in:
parent
a1f4fd32a1
commit
00b60e564e
@ -14,7 +14,7 @@ Binary to String Encode/Decode
|
||||
Assert that encoding type is valid. This needs to be kept up to date with the last item in the enum.
|
||||
***********************************************************************************************************************************/
|
||||
#define ASSERT_ENCODE_TYPE_VALID(type) \
|
||||
ASSERT(type <= encodeBase64);
|
||||
ASSERT(type <= encodeBase64Url);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Base64 encoding/decoding
|
||||
@ -224,6 +224,96 @@ decodeToBinSizeBase64(const char *source)
|
||||
FUNCTION_TEST_RETURN(destinationSize);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Base64 encoding
|
||||
***********************************************************************************************************************************/
|
||||
static const char encodeBase64LookupUrl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
|
||||
static void
|
||||
encodeToStrBase64Url(const unsigned char *source, size_t sourceSize, char *destination)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM_P(UCHARDATA, source);
|
||||
FUNCTION_TEST_PARAM(SIZE, sourceSize);
|
||||
FUNCTION_TEST_PARAM_P(CHARDATA, destination);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(source != NULL);
|
||||
ASSERT(destination != NULL);
|
||||
|
||||
unsigned int destinationIdx = 0;
|
||||
|
||||
// Encode the string from three bytes to four characters
|
||||
for (unsigned int sourceIdx = 0; sourceIdx < sourceSize; sourceIdx += 3)
|
||||
{
|
||||
// First encoded character is always used completely
|
||||
destination[destinationIdx++] = encodeBase64LookupUrl[source[sourceIdx] >> 2];
|
||||
|
||||
// If there is only one byte to encode then the second encoded character is only partly used
|
||||
if (sourceSize - sourceIdx == 1)
|
||||
{
|
||||
destination[destinationIdx++] = encodeBase64LookupUrl[(source[sourceIdx] & 0x03) << 4];
|
||||
}
|
||||
// Else if more than one byte to encode
|
||||
else
|
||||
{
|
||||
// If there is more than one byte to encode then the second encoded character is used completely
|
||||
destination[destinationIdx++] =
|
||||
encodeBase64LookupUrl[((source[sourceIdx] & 0x03) << 4) | ((source[sourceIdx + 1] & 0xf0) >> 4)];
|
||||
|
||||
// If there are only two bytes to encode then the third encoded character is only partly used
|
||||
if (sourceSize - sourceIdx == 2)
|
||||
{
|
||||
destination[destinationIdx++] = encodeBase64LookupUrl[(source[sourceIdx + 1] & 0x0f) << 2];
|
||||
}
|
||||
// Else the third and fourth encoded characters are used completely
|
||||
else
|
||||
{
|
||||
destination[destinationIdx++] =
|
||||
encodeBase64LookupUrl[((source[sourceIdx + 1] & 0x0f) << 2) | ((source[sourceIdx + 2] & 0xc0) >> 6)];
|
||||
destination[destinationIdx++] = encodeBase64LookupUrl[source[sourceIdx + 2] & 0x3f];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Zero-terminate the string
|
||||
destination[destinationIdx] = 0;
|
||||
|
||||
FUNCTION_TEST_RETURN_VOID();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
static size_t
|
||||
encodeToStrSizeBase64Url(size_t sourceSize)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(SIZE, sourceSize);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
// Calculate how many groups of three are in the source. Each group of three is encoded with four bytes.
|
||||
size_t encodeTotal = sourceSize / 3 * 4;
|
||||
|
||||
// Dertermine additional required bytes for the partial group, if any
|
||||
switch (sourceSize % 3)
|
||||
{
|
||||
// One byte requires two characters to encode
|
||||
case 1:
|
||||
encodeTotal += 2;
|
||||
break;
|
||||
|
||||
// Two bytes require three characters to encode
|
||||
case 2:
|
||||
encodeTotal += 3;
|
||||
break;
|
||||
|
||||
// If mod is zero then sourceSize was evenly divisible and no additional bytes are required
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN(encodeTotal);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Generic encoding/decoding
|
||||
***********************************************************************************************************************************/
|
||||
@ -244,6 +334,10 @@ encodeToStr(EncodeType type, const unsigned char *source, size_t sourceSize, cha
|
||||
case encodeBase64:
|
||||
encodeToStrBase64(source, sourceSize, destination);
|
||||
break;
|
||||
|
||||
case encodeBase64Url:
|
||||
encodeToStrBase64Url(source, sourceSize, destination);
|
||||
break;
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN_VOID();
|
||||
@ -267,6 +361,10 @@ encodeToStrSize(EncodeType type, size_t sourceSize)
|
||||
case encodeBase64:
|
||||
destinationSize = encodeToStrSizeBase64(sourceSize);
|
||||
break;
|
||||
|
||||
case encodeBase64Url:
|
||||
destinationSize = encodeToStrSizeBase64Url(sourceSize);
|
||||
break;
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN(destinationSize);
|
||||
@ -289,6 +387,9 @@ decodeToBin(EncodeType type, const char *source, unsigned char *destination)
|
||||
case encodeBase64:
|
||||
decodeToBinBase64(source, destination);
|
||||
break;
|
||||
|
||||
default:
|
||||
ASSERT_MSG("unsupported");
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN_VOID();
|
||||
@ -312,6 +413,9 @@ decodeToBinSize(EncodeType type, const char *source)
|
||||
case encodeBase64:
|
||||
destinationSize = decodeToBinSizeBase64(source);
|
||||
break;
|
||||
|
||||
default:
|
||||
ASSERT_MSG("unsupported");
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN(destinationSize);
|
||||
|
@ -12,6 +12,7 @@ Encoding types
|
||||
typedef enum
|
||||
{
|
||||
encodeBase64,
|
||||
encodeBase64Url,
|
||||
} EncodeType;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
|
@ -115,7 +115,7 @@ unit:
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: encode
|
||||
total: 1
|
||||
total: 2
|
||||
|
||||
coverage:
|
||||
- common/encode
|
||||
|
@ -98,5 +98,46 @@ testRun(void)
|
||||
"base64 last character must be '=' if second to last is");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("base64url"))
|
||||
{
|
||||
TEST_TITLE("encode");
|
||||
|
||||
const unsigned char *encode = (const unsigned char *)"string_to_encode\r\n";
|
||||
char destinationEncode[256];
|
||||
|
||||
encodeToStr(encodeBase64Url, encode, 1, destinationEncode);
|
||||
TEST_RESULT_Z(destinationEncode, "cw", "1 character encode");
|
||||
TEST_RESULT_UINT(encodeToStrSize(encodeBase64Url, 1), strlen(destinationEncode), "check size");
|
||||
|
||||
encodeToStr(encodeBase64Url, encode, 2, destinationEncode);
|
||||
TEST_RESULT_Z(destinationEncode, "c3Q", "2 character encode");
|
||||
TEST_RESULT_UINT(encodeToStrSize(encodeBase64Url, 2), strlen(destinationEncode), "check size");
|
||||
|
||||
encodeToStr(encodeBase64Url, encode, 3, destinationEncode);
|
||||
TEST_RESULT_Z(destinationEncode, "c3Ry", "3 character encode");
|
||||
TEST_RESULT_UINT(encodeToStrSize(encodeBase64Url, 3), strlen(destinationEncode), "check size");
|
||||
|
||||
encodeToStr(encodeBase64Url, encode, strlen((char *)encode) - 2, destinationEncode);
|
||||
TEST_RESULT_Z(destinationEncode, "c3RyaW5nX3RvX2VuY29kZQ", "encode full string");
|
||||
TEST_RESULT_UINT(encodeToStrSize(encodeBase64Url, strlen((char *)encode) - 2), strlen(destinationEncode), "check size");
|
||||
|
||||
encodeToStr(encodeBase64Url, encode, strlen((char *)encode), destinationEncode);
|
||||
TEST_RESULT_Z(destinationEncode, "c3RyaW5nX3RvX2VuY29kZQ0K", "encode full string with \\r\\n");
|
||||
TEST_RESULT_UINT(encodeToStrSize(encodeBase64Url, strlen((char *)encode)), strlen(destinationEncode), "check size");
|
||||
|
||||
encodeToStr(encodeBase64Url, encode, strlen((char *)encode) + 1, destinationEncode);
|
||||
TEST_RESULT_Z(destinationEncode, "c3RyaW5nX3RvX2VuY29kZQ0KAA", "encode full string with \\r\\n and null");
|
||||
TEST_RESULT_UINT(encodeToStrSize(encodeBase64Url, strlen((char *)encode) + 1), strlen(destinationEncode), "check size");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("decode unsupported");
|
||||
|
||||
unsigned char destinationDecode[256];
|
||||
|
||||
TEST_ERROR(decodeToBinSize(encodeBase64Url, "c3"), AssertError, "unsupported");
|
||||
TEST_ERROR(decodeToBin(encodeBase64Url, "c3", destinationDecode), AssertError, "unsupported");
|
||||
}
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user