Skip to content

Commit

Permalink
Add implementation for AES CBC in Sys.Security.Crypto (#2888)
Browse files Browse the repository at this point in the history
  • Loading branch information
josesimoes authored Jul 30, 2024
1 parent a384668 commit 775c2c2
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@ static const CLR_RT_MethodHandler method_lookup[] =
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
Library_nf_sys_sec_cryptography_System_Security_Cryptography_Aes::EncryptAesEcb___SZARRAY_U1__SZARRAY_U1,
Library_nf_sys_sec_cryptography_System_Security_Cryptography_Aes::DecryptAesEcb___SZARRAY_U1__SZARRAY_U1,
Library_nf_sys_sec_cryptography_System_Security_Cryptography_Aes::EncryptAesCbc___SZARRAY_U1__SZARRAY_U1,
Library_nf_sys_sec_cryptography_System_Security_Cryptography_Aes::DecryptAesCbc___SZARRAY_U1__SZARRAY_U1,
NULL,
NULL,
NULL,
Expand All @@ -33,9 +38,9 @@ static const CLR_RT_MethodHandler method_lookup[] =
const CLR_RT_NativeAssemblyData g_CLR_AssemblyNative_nanoFramework_System_Security_Cryptography =
{
"nanoFramework.System.Security.Cryptography",
0xF4AEFE6C,
0x343142CA,
method_lookup,
{ 100, 0, 0, 2 }
{ 100, 0, 0, 3 }
};

// clang-format on
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,34 @@
#include <nf_mbedtls_config.h>
#endif


#include "mbedtls/version.h"
#include <mbedtls/platform.h>
#include <mbedtls/md.h>
#if MBEDTLS_VERSION_MAJOR == 2
#include <mbedtls/md_internal.h>
#endif
#include <mbedtls/aes.h>
#include <mbedtls/cipher.h>

typedef enum __nfpack CipherMode
{
CipherMode_None = 0,
CipherMode_CBC = 1,
CipherMode_ECB = 2,
} CipherMode;

struct Library_nf_sys_sec_cryptography_System_Security_Cryptography_Aes
{
static const int FIELD___mode = 1;
static const int FIELD___key = 2;
static const int FIELD___iv = 3;

NANOCLR_NATIVE_DECLARE(EncryptAesEcb___SZARRAY_U1__SZARRAY_U1);
NANOCLR_NATIVE_DECLARE(DecryptAesEcb___SZARRAY_U1__SZARRAY_U1);
NANOCLR_NATIVE_DECLARE(EncryptAesCbc___SZARRAY_U1__SZARRAY_U1);
NANOCLR_NATIVE_DECLARE(DecryptAesCbc___SZARRAY_U1__SZARRAY_U1);

//--//
static HRESULT EncryptDecrypt(
CLR_RT_StackFrame &stack,
mbedtls_cipher_type_t cipher,
mbedtls_operation_t operation);
};

struct Library_nf_sys_sec_cryptography_System_Security_Cryptography_HMACSHA256
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,75 +10,58 @@ HRESULT Library_nf_sys_sec_cryptography_System_Security_Cryptography_Aes::Encryp
{
NANOCLR_HEADER();

mbedtls_aes_context encodeContext;
NANOCLR_CHECK_HRESULT(EncryptDecrypt(stack, MBEDTLS_CIPHER_AES_128_ECB, MBEDTLS_ENCRYPT));

CLR_RT_HeapBlock_Array *keyArray;
CLR_RT_HeapBlock_Array *plainTextArray;
CLR_RT_HeapBlock_Array *cipherTextArray;
CLR_RT_HeapBlock *pThis;

pThis = (CLR_RT_HeapBlock *)stack.This();
FAULT_ON_NULL(pThis);
NANOCLR_NOCLEANUP();
}

// grab key and check for NULL
keyArray = pThis[FIELD___key].DereferenceArray();
if (keyArray == NULL)
{
NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_OPERATION);
}
HRESULT Library_nf_sys_sec_cryptography_System_Security_Cryptography_Aes::DecryptAesEcb___SZARRAY_U1__SZARRAY_U1(
CLR_RT_StackFrame &stack)
{
NANOCLR_HEADER();

// grab plain text and check for NULL
plainTextArray = stack.Arg1().DereferenceArray();
FAULT_ON_NULL_ARG(plainTextArray);
NANOCLR_CHECK_HRESULT(EncryptDecrypt(stack, MBEDTLS_CIPHER_AES_128_ECB, MBEDTLS_DECRYPT));

// data has to be multiple of 16
if (plainTextArray->m_numOfElements % 16 != 0)
{
NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
}

// create the return array (same length as the input)
stack.PushValueAndClear();
NANOCLR_CHECK_HRESULT(CLR_RT_HeapBlock_Array::CreateInstance(
stack.TopValue(),
plainTextArray->m_numOfElements,
g_CLR_RT_WellKnownTypes.m_UInt8));
cipherTextArray = stack.TopValue().DereferenceArray();
NANOCLR_NOCLEANUP();
}

// init mbedtls context
mbedtls_aes_init(&encodeContext);
HRESULT Library_nf_sys_sec_cryptography_System_Security_Cryptography_Aes::EncryptAesCbc___SZARRAY_U1__SZARRAY_U1(
CLR_RT_StackFrame &stack)
{
NANOCLR_HEADER();

if (mbedtls_aes_setkey_enc(&encodeContext, keyArray->GetFirstElement(), keyArray->m_numOfElements * 8) ==
MBEDTLS_ERR_AES_INVALID_KEY_LENGTH)
{
NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
}
NANOCLR_CHECK_HRESULT(EncryptDecrypt(stack, MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_ENCRYPT));

if (mbedtls_aes_crypt_ecb(
&encodeContext,
MBEDTLS_AES_ENCRYPT,
plainTextArray->GetFirstElement(),
cipherTextArray->GetFirstElement()) != 0)
{
NANOCLR_SET_AND_LEAVE(CLR_E_FAIL);
}
NANOCLR_NOCLEANUP();
}

NANOCLR_CLEANUP();
HRESULT Library_nf_sys_sec_cryptography_System_Security_Cryptography_Aes::DecryptAesCbc___SZARRAY_U1__SZARRAY_U1(
CLR_RT_StackFrame &stack)
{
NANOCLR_HEADER();

// make sure nothing is left in memory
mbedtls_aes_free(&encodeContext);
NANOCLR_CHECK_HRESULT(EncryptDecrypt(stack, MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_DECRYPT));

NANOCLR_CLEANUP_END();
NANOCLR_NOCLEANUP();
}

HRESULT Library_nf_sys_sec_cryptography_System_Security_Cryptography_Aes::DecryptAesEcb___SZARRAY_U1__SZARRAY_U1(
CLR_RT_StackFrame &stack)
HRESULT Library_nf_sys_sec_cryptography_System_Security_Cryptography_Aes::EncryptDecrypt(
CLR_RT_StackFrame &stack,
mbedtls_cipher_type_t cipher,
mbedtls_operation_t operation)
{
NANOCLR_HEADER();

mbedtls_aes_context decodeContext;
mbedtls_cipher_context_t ctx;
mbedtls_cipher_info_t const *cipherInfo;
size_t outputLength = 0;
size_t ivLength = 0;
uint16_t passCount = 0;
uint8_t *ivCopy = NULL;
uint8_t *workBuffer = NULL;

CLR_RT_HeapBlock_Array *keyArray;
CLR_RT_HeapBlock_Array *ivArray;
CLR_RT_HeapBlock_Array *plainTextArray;
CLR_RT_HeapBlock_Array *cipherTextArray;
CLR_RT_HeapBlock *pThis;
Expand All @@ -93,46 +76,117 @@ HRESULT Library_nf_sys_sec_cryptography_System_Security_Cryptography_Aes::Decryp
NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_OPERATION);
}

// grab cipher text and check for NULL
cipherTextArray = stack.Arg1().DereferenceArray();
FAULT_ON_NULL_ARG(cipherTextArray);
// grab IV
// (expecting this to be filled with the IV from managed code)
ivArray = pThis[FIELD___iv].DereferenceArray();

// need to check if IV is NULL (AES doesn't require an IV)
if (ivArray != NULL)
{
// need a copy of the IV because it will be modified by mbedtls
ivCopy = (uint8_t *)platform_malloc(ivArray->m_numOfElements);

if (ivCopy == NULL)
{
NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_MEMORY);
}

memcpy((void *)ivCopy, ivArray->GetFirstElement(), ivArray->m_numOfElements);

ivLength = ivArray->m_numOfElements;
}

// grab plain text and check for NULL
plainTextArray = stack.Arg1().DereferenceArray();
FAULT_ON_NULL_ARG(plainTextArray);

// data has to be multiple of 16
if (cipherTextArray->m_numOfElements % 16 != 0)
if (plainTextArray->m_numOfElements % 16 != 0)
{
NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
}

// create the return array (same length as the input)
// per MbedTLS requirements, the output buffer has to be at least the block size longer than the input buffer
stack.PushValueAndClear();
NANOCLR_CHECK_HRESULT(CLR_RT_HeapBlock_Array::CreateInstance(
stack.TopValue(),
cipherTextArray->m_numOfElements,
plainTextArray->m_numOfElements,
g_CLR_RT_WellKnownTypes.m_UInt8));
plainTextArray = stack.TopValue().DereferenceArray();

// get a reference to the array to return
cipherTextArray = stack.TopValue().DereferenceArray();

// init mbedtls context
mbedtls_aes_init(&decodeContext);
mbedtls_cipher_init(&ctx);

if (mbedtls_aes_setkey_dec(&decodeContext, keyArray->GetFirstElement(), keyArray->m_numOfElements * 8) ==
MBEDTLS_ERR_AES_INVALID_KEY_LENGTH)
// set up the cipher
cipherInfo = mbedtls_cipher_info_from_type(cipher);
mbedtls_cipher_setup(&ctx, cipherInfo);

// need a work buffer to hold the encrypted data
workBuffer = (uint8_t *)platform_malloc(plainTextArray->m_numOfElements + cipherInfo->private_block_size);

if (workBuffer == NULL)
{
NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_MEMORY);
}

// set the padding mode to none
mbedtls_cipher_set_padding_mode(&ctx, MBEDTLS_PADDING_NONE);

if (mbedtls_cipher_setkey(&ctx, keyArray->GetFirstElement(), keyArray->m_numOfElements * 8, operation) != 0)
{
NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
}

if (mbedtls_aes_crypt_ecb(
&decodeContext,
MBEDTLS_AES_DECRYPT,
cipherTextArray->GetFirstElement(),
plainTextArray->GetFirstElement()) != 0)
if (cipher == MBEDTLS_CIPHER_AES_128_ECB && plainTextArray->m_numOfElements > 16)
{
NANOCLR_SET_AND_LEAVE(CLR_E_FAIL);
// need to iterate through the plain text array, encrypting 16 bytes at a time
while (passCount < plainTextArray->m_numOfElements / 16)
{
mbedtls_cipher_crypt(
&ctx,
ivCopy,
ivLength,
plainTextArray->GetElement(passCount * 16),
16,
&workBuffer[passCount * 16],
&outputLength);

passCount++;
}
}
else
{
// encrypt the data
mbedtls_cipher_crypt(
&ctx,
ivCopy,
ivLength,
plainTextArray->GetFirstElement(),
plainTextArray->m_numOfElements,
workBuffer,
&outputLength);
}

// make sure nothing is left in memory
mbedtls_cipher_free(&ctx);

// copy the output buffer to the return array
memcpy(cipherTextArray->GetFirstElement(), workBuffer, plainTextArray->m_numOfElements);

NANOCLR_CLEANUP();

// make sure nothing is left in memory
mbedtls_aes_free(&decodeContext);
if (ivCopy != NULL)
{
platform_free(ivCopy);
}

if (workBuffer != NULL)
{
platform_free(workBuffer);
}

NANOCLR_CLEANUP_END();
}

0 comments on commit 775c2c2

Please sign in to comment.