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

Add implementation for AES CBC in Sys.Security.Crypto #2888

Merged
merged 7 commits into from
Jul 30, 2024
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
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();
}