Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix - Changed encoding from Base64 (with padding) to Base64URL (no padding) for SenML JSON format #710

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions core/internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -446,10 +446,10 @@ int utils_textToObjLink(const uint8_t * buffer,
uint16_t * objectId,
uint16_t * objectInstanceId);
void utils_copyValue(void * dst, const void * src, size_t len);
size_t utils_base64GetSize(size_t dataLen);
size_t utils_base64Encode(const uint8_t * dataP, size_t dataLen, uint8_t * bufferP, size_t bufferLen);
size_t utils_base64GetSize(size_t dataLen, bool withPadding);
size_t utils_base64Encode(const uint8_t * dataP, size_t dataLen, uint8_t * bufferP, size_t bufferLen, bool useB64UrlAlphabet, bool withPadding);
size_t utils_base64GetDecodedSize(const char * dataP, size_t dataLen);
size_t utils_base64Decode(const char * dataP, size_t dataLen, uint8_t * bufferP, size_t bufferLen);
size_t utils_base64Decode(const char * dataP, size_t dataLen, uint8_t * bufferP, size_t bufferLen, bool useB64UrlAlphabet);
#ifdef LWM2M_CLIENT_MODE
lwm2m_server_t * utils_findServer(lwm2m_context_t * contextP, void * fromSessionH);
lwm2m_server_t * utils_findBootstrapServer(lwm2m_context_t * contextP, void * fromSessionH);
Expand Down
102 changes: 73 additions & 29 deletions core/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -960,38 +960,65 @@ static char b64Alphabet[64] =
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};

static char b64UrlAlphabet[64] =
{
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
};

static void prv_encodeBlock(const uint8_t input[3],
uint8_t output[4])
uint8_t output[4],
const char *base64Alphabet)
{
output[0] = b64Alphabet[input[0] >> 2];
output[1] = b64Alphabet[((input[0] & 0x03) << 4) | (input[1] >> 4)];
output[2] = b64Alphabet[((input[1] & 0x0F) << 2) | (input[2] >> 6)];
output[3] = b64Alphabet[input[2] & 0x3F];
output[0] = base64Alphabet[input[0] >> 2];
output[1] = base64Alphabet[((input[0] & 0x03) << 4) | (input[1] >> 4)];
output[2] = base64Alphabet[((input[1] & 0x0F) << 2) | (input[2] >> 6)];
output[3] = base64Alphabet[input[2] & 0x3F];
}

size_t utils_base64GetSize(size_t dataLen)
size_t utils_base64GetSize(size_t dataLen, bool withPadding)
{
size_t result_len;

result_len = 4 * (dataLen / 3);
if (dataLen % 3) result_len += 4;

if(withPadding)
{
result_len = 4 * (dataLen / 3);
if (dataLen % 3) result_len += 4;
}
else
{
result_len = (dataLen * 4) / 3;
if(dataLen % 3) result_len += 1;
}
return result_len;
}

size_t utils_base64Encode(const uint8_t * dataP,
size_t dataLen,
uint8_t * bufferP,
size_t bufferLen)
size_t bufferLen,
bool useB64UrlAlphabet,
bool withPadding)
{
unsigned int data_index;
unsigned int result_index;
size_t result_len;
char *base64Alphabet = NULL;

result_len = utils_base64GetSize(dataLen);
result_len = utils_base64GetSize(dataLen, withPadding);

if (result_len > bufferLen) return 0;

if(useB64UrlAlphabet)
{
base64Alphabet = b64UrlAlphabet;
}
else
{
base64Alphabet = b64Alphabet;
}

data_index = 0;
result_index = 0;
while (data_index < dataLen)
Expand All @@ -1002,19 +1029,25 @@ size_t utils_base64Encode(const uint8_t * dataP,
// should never happen
break;
case 1:
bufferP[result_index] = b64Alphabet[dataP[data_index] >> 2];
bufferP[result_index + 1] = b64Alphabet[(dataP[data_index] & 0x03) << 4];
bufferP[result_index + 2] = PRV_B64_PADDING;
bufferP[result_index + 3] = PRV_B64_PADDING;
bufferP[result_index] = base64Alphabet[dataP[data_index] >> 2];
bufferP[result_index + 1] = base64Alphabet[(dataP[data_index] & 0x03) << 4];
if(withPadding)
{
bufferP[result_index + 2] = PRV_B64_PADDING;
bufferP[result_index + 3] = PRV_B64_PADDING;
}
break;
case 2:
bufferP[result_index] = b64Alphabet[dataP[data_index] >> 2];
bufferP[result_index + 1] = b64Alphabet[(dataP[data_index] & 0x03) << 4 | (dataP[data_index + 1] >> 4)];
bufferP[result_index + 2] = b64Alphabet[(dataP[data_index + 1] & 0x0F) << 2];
bufferP[result_index + 3] = PRV_B64_PADDING;
bufferP[result_index] = base64Alphabet[dataP[data_index] >> 2];
bufferP[result_index + 1] = base64Alphabet[(dataP[data_index] & 0x03) << 4 | (dataP[data_index + 1] >> 4)];
bufferP[result_index + 2] = base64Alphabet[(dataP[data_index + 1] & 0x0F) << 2];
if(withPadding)
{
bufferP[result_index + 3] = PRV_B64_PADDING;
}
break;
default:
prv_encodeBlock(dataP + data_index, bufferP + result_index);
prv_encodeBlock(dataP + data_index, bufferP + result_index, base64Alphabet);
break;
}
data_index += 3;
Expand Down Expand Up @@ -1055,18 +1088,29 @@ size_t utils_base64GetDecodedSize(const char * dataP, size_t dataLen)
return result;
}

static uint8_t prv_base64Value(char digit)
static uint8_t prv_base64Value(char digit, bool useB64UrlAlphabet)
{
uint8_t result = 0xFF;
if (digit >= 'A' && digit <= 'Z') result = digit - 'A';
else if (digit >= 'a' && digit <= 'z') result = digit - 'a' + 26;
else if (digit >= '0' && digit <= '9') result = digit - '0' + 52;
else if (digit == '+') result = 62;
else if (digit == '/') result = 63;
else
{
if(useB64UrlAlphabet)
{
if (digit == '-') result = 62;
else if (digit == '_') result = 63;
}
else
{
if (digit == '+') result = 62;
else if (digit == '/') result = 63;
}
}
return result;
}

size_t utils_base64Decode(const char * dataP, size_t dataLen, uint8_t * bufferP, size_t bufferLen)
size_t utils_base64Decode(const char * dataP, size_t dataLen, uint8_t * bufferP, size_t bufferLen, bool useB64UrlAlphabet)
{
size_t dataIndex;
size_t bufferIndex;
Expand All @@ -1080,23 +1124,23 @@ size_t utils_base64Decode(const char * dataP, size_t dataLen, uint8_t * bufferP,
{
uint8_t v1, v2, v3, v4;
if (dataLen - dataIndex < 2) return 0;
v1 = prv_base64Value(dataP[dataIndex++]);
v1 = prv_base64Value(dataP[dataIndex++], useB64UrlAlphabet);
if (v1 >= 64) return 0;
v2 = prv_base64Value(dataP[dataIndex++]);
v2 = prv_base64Value(dataP[dataIndex++], useB64UrlAlphabet);
if (v2 >= 64) return 0;
bufferP[bufferIndex++] = (v1 << 2) + (v2 >> 4);
if (dataIndex < dataLen)
{
if (dataP[dataIndex] != PRV_B64_PADDING)
{
v3 = prv_base64Value(dataP[dataIndex++]);
v3 = prv_base64Value(dataP[dataIndex++], useB64UrlAlphabet);
if (v3 >= 64) return 0;
bufferP[bufferIndex++] = (v2 << 4) + (v3 >> 2);
if (dataIndex < dataLen)
{
if (dataP[dataIndex] != PRV_B64_PADDING)
{
v4 = prv_base64Value(dataP[dataIndex++]);
v4 = prv_base64Value(dataP[dataIndex++], useB64UrlAlphabet);
if (v4 >= 64) return 0;
bufferP[bufferIndex++] = (v3 << 6) + v4;
}
Expand Down
4 changes: 2 additions & 2 deletions data/data.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ static int prv_textSerialize(lwm2m_data_t * dataP,
{
size_t length;

length = utils_base64GetSize(dataP->value.asBuffer.length);
length = utils_base64GetSize(dataP->value.asBuffer.length, false);
*bufferP = (uint8_t *)lwm2m_malloc(length);
if (*bufferP == NULL) return 0;
length = utils_base64Encode(dataP->value.asBuffer.buffer, dataP->value.asBuffer.length, *bufferP, length);
length = utils_base64Encode(dataP->value.asBuffer.buffer, dataP->value.asBuffer.length, *bufferP, length, true, false);
if (length == 0)
{
lwm2m_free(*bufferP);
Expand Down
2 changes: 1 addition & 1 deletion data/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,7 @@ static int prv_serializeValue(lwm2m_data_t * tlvP,
memcpy(buffer, JSON_ITEM_STRING_BEGIN, JSON_ITEM_STRING_BEGIN_SIZE);
head = JSON_ITEM_STRING_BEGIN_SIZE;

res = utils_base64Encode(tlvP->value.asBuffer.buffer, tlvP->value.asBuffer.length, buffer+head, bufferLen - head);
res = utils_base64Encode(tlvP->value.asBuffer.buffer, tlvP->value.asBuffer.length, buffer+head, bufferLen - head, false, true);
if (tlvP->value.asBuffer.length != 0 && res == 0) return -1;
head += res;

Expand Down
7 changes: 5 additions & 2 deletions data/senml_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,8 @@ static bool prv_convertValue(const _record_t * recordP,
dataLength = utils_base64Decode((const char *)recordP->value.value.asBuffer.buffer,
recordP->value.value.asBuffer.length,
data,
dataLength);
dataLength,
true);
if (dataLength)
{
lwm2m_data_encode_opaque(data, dataLength, targetP);
Expand Down Expand Up @@ -902,7 +903,9 @@ static int prv_serializeValue(const lwm2m_data_t * tlvP,
res = utils_base64Encode(tlvP->value.asBuffer.buffer,
tlvP->value.asBuffer.length,
buffer+head,
bufferLen - head);
bufferLen - head,
true,
false);
if (res < tlvP->value.asBuffer.length) return -1;
head += res;
}
Expand Down
58 changes: 43 additions & 15 deletions tests/convert_numbers_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -419,29 +419,55 @@ static void test_utils_objLinkToText(void)
CU_ASSERT_EQUAL(utils_objLinkToText(0, 0, text, 3), 3)
}

static void test_utils_base64_1(const uint8_t *binary, size_t binaryLength, const char *base64)
static void test_utils_base64WithPadding_1(const uint8_t *binary, size_t binaryLength, const char *base64WithPadding)
{
uint8_t encodeBuffer[8];
uint8_t decodeBuffer[6];
size_t base64Length = strlen(base64);
uint8_t encodeBuffer[12];
uint8_t decodeBuffer[7];
size_t base64WithPaddingLength = strlen(base64WithPadding);
memset(encodeBuffer, 0, sizeof(encodeBuffer));
memset(decodeBuffer, 0xFF, sizeof(decodeBuffer));
CU_ASSERT_EQUAL(utils_base64GetSize(binaryLength), base64Length)
CU_ASSERT_EQUAL(utils_base64GetDecodedSize(base64, base64Length), binaryLength)
CU_ASSERT_EQUAL(utils_base64Encode(binary, binaryLength, encodeBuffer, sizeof(encodeBuffer)), base64Length)
CU_ASSERT_NSTRING_EQUAL(encodeBuffer, base64, base64Length)
CU_ASSERT_EQUAL(utils_base64Decode(base64, base64Length, decodeBuffer, sizeof(decodeBuffer)), binaryLength)
CU_ASSERT_EQUAL(utils_base64GetSize(binaryLength, true), base64WithPaddingLength)
CU_ASSERT_EQUAL(utils_base64GetDecodedSize(base64WithPadding, base64WithPaddingLength), binaryLength)
CU_ASSERT_EQUAL(utils_base64Encode(binary, binaryLength, encodeBuffer, sizeof(encodeBuffer), false, true), base64WithPaddingLength)
CU_ASSERT_NSTRING_EQUAL(encodeBuffer, base64WithPadding, base64WithPaddingLength)
CU_ASSERT_EQUAL(utils_base64Decode(base64WithPadding, base64WithPaddingLength, decodeBuffer, sizeof(decodeBuffer), false), binaryLength)
CU_ASSERT_EQUAL(memcmp(decodeBuffer, binary, binaryLength), 0)
}

static void test_utils_base64(void)
static void test_utils_base64UrlNoPadding_1(const uint8_t *binary, size_t binaryLength, const char *base64UrlNoPadding)
{
uint8_t binary[] = { 0, 1, 2, 3, 4, 5 };
const char * base64[] = { "AA==", "AAE=", "AAEC", "AAECAw==", "AAECAwQ=", "AAECAwQF" };
uint8_t encodeBuffer[10];
uint8_t decodeBuffer[7];
size_t base64UrlNoPaddingLength = strlen(base64UrlNoPadding);
memset(encodeBuffer, 0, sizeof(encodeBuffer));
memset(decodeBuffer, 0xFF, sizeof(decodeBuffer));
CU_ASSERT_EQUAL(utils_base64GetSize(binaryLength, false), base64UrlNoPaddingLength)
CU_ASSERT_EQUAL(utils_base64GetDecodedSize(base64UrlNoPadding, base64UrlNoPaddingLength), binaryLength)
CU_ASSERT_EQUAL(utils_base64Encode(binary, binaryLength, encodeBuffer, sizeof(encodeBuffer), true, false), base64UrlNoPaddingLength)
CU_ASSERT_NSTRING_EQUAL(encodeBuffer, base64UrlNoPadding, base64UrlNoPaddingLength)
CU_ASSERT_EQUAL(utils_base64Decode(base64UrlNoPadding, base64UrlNoPaddingLength, decodeBuffer, sizeof(decodeBuffer), true), binaryLength)
CU_ASSERT_EQUAL(memcmp(decodeBuffer, binary, binaryLength), 0)
}

static void test_utils_base64WithPadding(void)
{
uint8_t binary[] = { 0, 1, 2, 3, 4, 5, 0xFF };
const char * base64WithPadding[] = { "AA==", "AAE=", "AAEC", "AAECAw==", "AAECAwQ=", "AAECAwQF", "AAECAwQF/w==" };
size_t i;
for (i = 0; i < sizeof(binary); i++)
{
test_utils_base64WithPadding_1(binary, i+1, base64WithPadding[i]);
}
}

static void test_utils_base64UrlNoPadding(void)
{
uint8_t binary[] = { 0, 1, 2, 3, 4, 5, 0xFF };
const char * base64UrlNoPadding[] = { "AA", "AAE", "AAEC", "AAECAw", "AAECAwQ", "AAECAwQF", "AAECAwQF_w" };
size_t i;
for (i = 0; i < sizeof(binary); i++)
{
test_utils_base64_1(binary, i+1, base64[i]);
test_utils_base64UrlNoPadding_1(binary, i+1, base64UrlNoPadding[i]);
}
}

Expand All @@ -458,11 +484,13 @@ static struct TestTable table[] = {
{ "test of utils_floatToText()", test_utils_floatToText },
{ "test of utils_floatToText(exponential)", test_utils_floatToTextExponential },
{ "test of utils_objLinkToText()", test_utils_objLinkToText },
{ "test of base64 functions", test_utils_base64 },
{ "test of base64 (with padding) functions", test_utils_base64WithPadding },
{ "test of base64Url (without padding) functions", test_utils_base64UrlNoPadding },
{ NULL, NULL },
};

CU_ErrorCode create_convert_numbers_suit(void) {
CU_ErrorCode create_convert_numbers_suit(void)
{
CU_pSuite pSuite = NULL;

pSuite = CU_add_suite("Suite_ConvertNumbers", NULL, NULL);
Expand Down