Skip to content

Commit

Permalink
Changed encoding to Base64URL (no padding)
Browse files Browse the repository at this point in the history
Fix - Changed encoding from Base64 (with padding) to Base64URL (no paddi
ng) for data value 'vd' over SenML JSON format.

According to the SenML JSON specification defined at RFC8428§4.3 the
Base64 URL safe alphabet must be used (without padding) for Data Value
'vd':

"(*) Data Value is a base64-encoded string with the URL-safe alphabet as
defined in Section 5 of [RFC4648], with padding omitted. (In CBOR, the
octets in the Data Value are encoded using a definite-length byte
string, major type 2.)"

So, encoding and decoding has been changed from Base64 (with padding) to
Base64Url (without padding) for data value 'vd' over SenML JSON format.

Note that the behavior of the code in data/json.c has not changed, data
in JSON format will continue to be encoded in Base64 (with padding), see
eclipse-leshan/leshan#1444 (comment)

New tests has been added in order to test the Base64Url (without
padding) encode and decode functions, and the previous tests have been
modified by adding a new entry to be tested.

Fix: eclipse-wakaama#698
also see:
github.com/OpenMobileAlliance/OMA_LwM2M_for_Developers/issues/553
  • Loading branch information
parmi93 committed Jun 16, 2023
1 parent 5146594 commit 013b205
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 52 deletions.
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

0 comments on commit 013b205

Please sign in to comment.