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

Implemented Base64 encoding/decoding #934

Merged
merged 2 commits into from
Oct 23, 2018
Merged
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
3 changes: 3 additions & 0 deletions CMake/Modules/FindNF_CoreCLR.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ list(APPEND NF_CoreCLR_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/src/CLR/Startup)
list(APPEND NF_CoreCLR_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/src/CLR/Diagnostics)
list(APPEND NF_CoreCLR_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/src/CLR/Debugger)
list(APPEND NF_CoreCLR_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/src/CLR/Helpers/TinyPrintf)
list(APPEND NF_CoreCLR_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/src/CLR/Helpers/Base64)


# source files for nanoFramework Core, CoreLib and CLR startup
Expand Down Expand Up @@ -155,6 +156,7 @@ set(NF_CoreCLR_SRCS

# Helpers
printf.c
base64.c

# HAL
nanoHAL_Time.cpp
Expand Down Expand Up @@ -212,6 +214,7 @@ foreach(SRC_FILE ${NF_CoreCLR_SRCS})

# Helpers
${PROJECT_SOURCE_DIR}/src/CLR/Helpers/TinyPrintf
${PROJECT_SOURCE_DIR}/src/CLR/Helpers/Base64

# HAL
${PROJECT_SOURCE_DIR}/src/HAL
Expand Down
158 changes: 156 additions & 2 deletions src/CLR/CorLib/corlib_native_System_Convert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "corlib_native.h"
#include <ctype.h>
#include <base64.h>

HRESULT Library_corlib_native_System_Convert::NativeToInt64___STATIC__I8__STRING__BOOLEAN__I8__I8__I4( CLR_RT_StackFrame& stack )
{
Expand Down Expand Up @@ -291,7 +292,97 @@ HRESULT Library_corlib_native_System_Convert::ToBase64String___STATIC__STRING__S
{
NANOCLR_HEADER();

NANOCLR_SET_AND_LEAVE(stack.NotImplementedStub());
size_t outputLength;
char* outArray = NULL;
char* outArrayWitLineBreak = NULL;
uint8_t* inArrayPointer = NULL;
uint8_t lineBreakCount;
uint16_t offsetIndex = 0;
uint8_t count = 0;

CLR_RT_HeapBlock_Array* inArray = stack.Arg0().DereferenceArray();
size_t offset = (size_t)stack.Arg1().NumericByRef().s4;
size_t lenght = (size_t)stack.Arg2().NumericByRef().s4;
bool insertLineBreaks = (bool)stack.Arg3().NumericByRefConst().u1;

if(inArray == NULL) NANOCLR_SET_AND_LEAVE(CLR_E_ARGUMENT_NULL);

inArrayPointer = (uint8_t*)inArray->GetFirstElement();
inArrayPointer += (offset * sizeof(uint8_t));

// compute base64 string length
outputLength = 4 * ((lenght + 2) / 3);

// need malloc with base64 string length plus string terminator (+1)
outArray = (char*)platform_malloc(outputLength + 1);

// check if have allocation
if (outArray == NULL) NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_MEMORY);

// perform the operation
mbedtls_base64_encode( (unsigned char*)outArray, 0, &outputLength, inArrayPointer, lenght );

if(insertLineBreaks)
{
// get line break count (line break every 76 chars)
lineBreakCount = outputLength / 76;

// need malloc with base64 string length plus line breaks (line break is 2 char long: CR + LF) plus final line break
outArrayWitLineBreak = (char*)platform_malloc(outputLength + (lineBreakCount * 2) + 2);

for(int i = 0; i <= lineBreakCount; i++)
{
// how many chars to copy
if(outputLength > 76)
{
// first/next 76 chars
count = 76;
}
else
{
// last outputLength chars
count = outputLength;
}

// copy first/next count chars
// because we are using same offset for both arrays, we need to discount line break for tmp array
memcpy(outArrayWitLineBreak + offsetIndex, outArray + (offsetIndex - (i * 2)), count);

// remove copied chars from original output length if more than 76 chars still to be copied
if(outputLength >= 76)
{
// more chars

// adjust output length
outputLength -= 76;

// add line break
outArrayWitLineBreak[count + offsetIndex] = '\r';
outArrayWitLineBreak[count + offsetIndex + 1] = '\n';

// move offset for next copy (including line break +2)
offsetIndex += count + 2;
}
else
{
// move offset for last position
offsetIndex += count;
// reached end of array, add terminator
outArrayWitLineBreak[offsetIndex] = 0;
}
}
// set a return result in the stack argument using the appropriate SetResult according to the variable type (a string here)
NANOCLR_CHECK_HRESULT(stack.SetResult_String(outArrayWitLineBreak));
}
else
{
// set a return result in the stack argument using the appropriate SetResult according to the variable type (a string here)
NANOCLR_CHECK_HRESULT(stack.SetResult_String(outArray));
}

// need to free memory from arrays
platform_free((void*)outArray);
if(outArrayWitLineBreak != NULL) platform_free((void*)outArrayWitLineBreak);

NANOCLR_NOCLEANUP();
}
Expand All @@ -300,7 +391,70 @@ HRESULT Library_corlib_native_System_Convert::FromBase64CharArray___STATIC__SZAR
{
NANOCLR_HEADER();

NANOCLR_SET_AND_LEAVE(stack.NotImplementedStub());
size_t outputLength;
char* outArray = NULL;
uint16_t* inArrayPointerTmp = NULL;
uint8_t* inArrayPointer = NULL;
uint8_t charValue;
CLR_UINT8* returnArray;
int16_t i = 0;

CLR_RT_HeapBlock_Array* inArray = stack.Arg0().DereferenceArray();
size_t length = (size_t)stack.Arg1().NumericByRef().s4;

if(inArray == NULL) NANOCLR_SET_AND_LEAVE(CLR_E_ARGUMENT_NULL);

outputLength = length / 4 * 3;

// transform the 16 bits inArray to a 8 bits array so mbed knows how to convert it
inArrayPointerTmp = (uint16_t*)inArray->GetFirstElementUInt16();
inArrayPointer = (uint8_t*)inArray->GetFirstElement();
for(i = 0; i < (int16_t)length; i++)
{
*inArrayPointer = *inArrayPointerTmp;
inArrayPointer++;
inArrayPointerTmp++;
}

// pointer is pointing to the end
// point to last char in array and get it
inArrayPointer--;
charValue = *inArrayPointer;
// adjust output length
if (charValue == '=')
{
outputLength--;
}
// point to before last char and get it
inArrayPointer--;
charValue = *inArrayPointer;
// adjust output length
if (charValue == '=')
{
outputLength--;
}

// reset pointer position (-2 because above we already went back two positions)
inArrayPointer -= length - 2;

outArray = (char*)platform_malloc(outputLength + 1);
// check malloc success
if (outArray == NULL) NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_MEMORY);

// perform the operation
mbedtls_base64_decode( (unsigned char*)outArray, 0, &outputLength, inArrayPointer, length );

// create heap block array instance with appropriate size (the lenght of the output array) and type (byte which is uint8_t)
NANOCLR_CHECK_HRESULT(CLR_RT_HeapBlock_Array::CreateInstance( stack.PushValueAndClear() , outputLength, g_CLR_RT_WellKnownTypes.m_UInt8 ));

// get a pointer to the array in the heap block array just created
returnArray = stack.TopValue().DereferenceArray()->GetFirstElement();

// copy outArray to the returnArray
memcpy(returnArray, outArray, outputLength);

// need to free memory from outArray
platform_free(outArray);

NANOCLR_NOCLEANUP();
}
Expand Down
104 changes: 104 additions & 0 deletions src/CLR/Helpers/Base64/base64.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//
// Copyright (c) 2018 The nanoFramework project contributors
// Portions Copyright (c) https://www.mycplus.com. All Rights Reserved.
// See LICENSE file in the project root for full license information.
//

//////////////////////////////////////////////////////////////////////////////////////////////
// we are using the function declarations matching the mbedTLS ones BUT with weak attribute //
// if the image includes the mbedTLS, these will be replaced by the strong ones from there //
// thus there will be no duplicate code //
//////////////////////////////////////////////////////////////////////////////////////////////

#include "base64.h"

void build_decoding_table(char *decoding_table )
{
if(decoding_table != NULL)
{
for (int i = 0; i < 64; i++)
decoding_table[(unsigned char) base64_enc_map[i]] = i;
}
}

__nfweak int mbedtls_base64_encode(
unsigned char *dst,
size_t dlen,
size_t *olen,
const unsigned char *src, size_t slen )
{
// parameter not used
(void)dlen;

for (unsigned int i = 0, j = 0; i < slen;)
{
int octet_a = i < slen ? (unsigned char)src[i++] : 0;
int octet_b = i < slen ? (unsigned char)src[i++] : 0;
int octet_c = i < slen ? (unsigned char)src[i++] : 0;

int triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;

dst[j++] = base64_enc_map[(triple >> 3 * 6) & 0x3F];
dst[j++] = base64_enc_map[(triple >> 2 * 6) & 0x3F];
dst[j++] = base64_enc_map[(triple >> 1 * 6) & 0x3F];
dst[j++] = base64_enc_map[(triple >> 0 * 6) & 0x3F];
}

for (unsigned int i = 0; i < mod_table[slen % 3]; i++)
dst[*olen - 1 - i] = '=';

// add terminator
dst[*olen] = 0;

return 0;
}

__nfweak int mbedtls_base64_decode(
unsigned char *dst,
size_t dlen,
size_t *olen,
const unsigned char *src, size_t slen )
{
// parameter not used
(void)dlen;

char *decoding_table = (char*)platform_malloc(256);

if (decoding_table == NULL)
{
// OK to return anything but 0
// we are forced to use mbedTLS behaviour anyway
return -1;
}

build_decoding_table(decoding_table);

if (slen % 4 != 0)
{
// OK to return anything but 0
// we are forced to use mbedTLS behaviour anyway
return -1;
}

for (unsigned int i = 0, j = 0; i < slen;) {

int sextet_a = src[i] == '=' ? 0 & i++ : decoding_table[src[i++]];
int sextet_b = src[i] == '=' ? 0 & i++ : decoding_table[src[i++]];
int sextet_c = src[i] == '=' ? 0 & i++ : decoding_table[src[i++]];
int sextet_d = src[i] == '=' ? 0 & i++ : decoding_table[src[i++]];

int triple = (sextet_a << 3 * 6)
+ (sextet_b << 2 * 6)
+ (sextet_c << 1 * 6)
+ (sextet_d << 0 * 6);

if (j < *olen) dst[j++] = (triple >> 2 * 8) & 0xFF;
if (j < *olen) dst[j++] = (triple >> 1 * 8) & 0xFF;
if (j < *olen) dst[j++] = (triple >> 0 * 8) & 0xFF;
}

// free table
platform_free(decoding_table);

return( 0 );
}
46 changes: 46 additions & 0 deletions src/CLR/Helpers/Base64/base64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// Copyright (c) 2018 The nanoFramework project contributors
// Portions Copyright (c) ARM Limited. All Rights Reserved.
// See LICENSE file in the project root for full license information.
//

#ifndef __BASE64_H__
#define __BASE64_H__

#include <stdio.h>
#include <nanoWeak.h>
#include <nanoHAL_v2.h>

//////////////////////////////////////////////////////////////////////////////////////////////
// we are using the function declarations matching the mbedTLS ones BUT with weak attribute //
// if the image includes the mbedTLS, these will be replaced by the strong ones from there //
// thus there will be no duplicate code //
//////////////////////////////////////////////////////////////////////////////////////////////

#ifdef __cplusplus
extern "C" {
#endif

int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen,
const unsigned char *src, size_t slen );
int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
const unsigned char *src, size_t slen );

static const unsigned char base64_enc_map[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 const unsigned int mod_table[3] = {0, 2, 1};

#ifdef __cplusplus
}
#endif

#endif //__BASE64_H__