B4R Tutorial [B4x]: Exchange AES-256 encrypted messages between ESP32 and B4x

KMatle

Expert
Licensed User
This is about how to exchange AES256 encrypted messages incl. generated IV (initialization vector) between a ESP32 and B4x.

Notes:

- the ESP uses AES/CBC/NoPadding. The data must be padded (must have a length which is a multiple of 16). I've used a length of 256 to to get there (instead of adding byted to the next multiple of 16).
- I've used a length of 256 for the plaintext message (can be raw bytes, too) AND for the encrypted message. This is - of course - not accurate because a encrypted message is definetly longer than a non encrypted.
- the B4R code works 100% but may be optimized. I've used arraycopy2 to copy local values to globals
- I've left some logging in the Inline C code
- IV is generated for every message (so every new message looks different)
- add a salt (very easy) to make it 100% safe (it is recommended to use a dynamic IV and Salt)
- The B4J code can be used for B4A, too (I don't know if B4I can use the encryption lib)

AES best practice: https://www.b4x.com/android/forum/threads/b4x-aes-encryption-lessons-learned-best-practice.97927/

The next week I will work on RSA encryption for the ESP :)

B4J libs needed:

esp32b4x.JPG



B4R-Code (change the SSID and PW). The B4J project is attached. .

B4X:
#Region Project Attributes
    #AutoFlushLogs: True
    #CheckArrayBounds: True
    #StackBufferSize: 20000
    #DefineExtra: #define SKIP_B4RNEW
#End Region

Sub Process_Globals
    Public Serial1 As Serial
    Private wifi As ESP8266WiFi
    Private server As WiFiServerSocket
    Private astream As AsyncStreams
    Private ser As B4RSerializator
   
    Public bc As ByteConverter
   
    Public plaintext(256) As Byte 'Must be a multiple of 16 as PKCSNoPadding is used
    Public iv(16) As Byte 'initialization vector. Must be the same for enc- and decryption
    Public pw(32) As  Byte 'pw/key (will be hashed to 32 bytes)
    Public encrypted(256) As  Byte 'the encrypted message
   
   
End Sub

Private Sub AppStart
   
    Serial1.Initialize(115200)
   
    Log("*** B4R AppStart ***")
    DelayMicroseconds(2000)
    For i=1 To 10
        Log("Connecting to WiFi...")
        Log(i)
        wifi.Disconnect
       
        If wifi.Connect2("SSID","WiFiPW") 'Change to your network SSID and password...
            Log("Connected to wireless network.")
            Log("My ip: ", wifi.LocalIp)
            Exit
        Else
            Log("Failed to connect...")
        End If
    Next

    server.Initialize(51042, "server_NewConnection")
    server.Listen
   
    'Set pw/key
    Dim pws As String="MySecretKey"
    bc.ArrayCopy(pws.GetBytes,pw)
   
End Sub

Sub Server_NewConnection (NewSocket As WiFiSocket)
    Log("Client connected...")
    astream.MaxBufferSize=10000
    astream.InitializePrefix(NewSocket.Stream, False, "astream_NewData", "astream_Error")
End Sub

Public Sub AStream_NewData (Buffer() As Byte)
    Dim be(10) As Object
    Dim data() As Object = ser.ConvertBytesToArray(Buffer, be)
    Log("Received:")
    For Each o As Object In data
        Log(o)
    Next
   
    Select be(0)
        Case "INIT"
            If be(1) = "PW" And be(2)="1234" Then
                astream.Write(ser.ConvertArrayToBytes(Array("INIT", "OK")))
            Else
                astream.Write(ser.ConvertArrayToBytes(Array("INIT", "ERROR")))
            End If
        Case "AES"
            'Split IV from the message
            SplitIVMessage(bc.HexToBytes(data(1)))
               
'Decrypt message from B4x
            RunNative("Decrypt", Null)
            Log("Decrypted from B4J: ",bc.StringFromBytes(plaintext))

'Encrypt message to B4x
            'Reset Plaintext
            For I=0 To plaintext.Length-1
                plaintext(I)=0
            Next
            'Set Plaintext
            Dim MessText() As Byte = "ESP32 encrypted message"
            bc.ArrayCopy2(MessText,0,plaintext,0,MessText.Length)
            'Generate IV
            Dim iv2() As Byte=GenerateIV
            bc.ArrayCopy2(iv2,0,iv,0,16)
            RunNative("Encrypt", Null)
            'Concatinate IV and encrypted message
            Dim mc(272) As Byte = AddIVMessage(iv,encrypted)
            astream.Write(ser.ConvertArrayToBytes(Array("AES",bc.HexFromBytes(mc))))
           
    End Select
End Sub

Private Sub SplitIVMessage (IvMessage () As Byte)
   
    bc.ArrayCopy2(IvMessage,0,iv,0,16)
    Log("IV: ", bc.HexFromBytes(iv))
   
    Dim Mess(256) As Byte
    'Init Mess
    For i=0 To Mess.Length-1
        Mess(i)=0
    Next
   
    bc.ArrayCopy2(IvMessage,16,Mess,0,IvMessage.Length-16)
    bc.ArrayCopy2(Mess,0,encrypted,0,Mess.Length)
   
End Sub

Sub GenerateIV As Byte()
   
    Dim IVB(16) As Byte
    For i=0 To 15
        IVB(i)=Rnd(0,256)
    Next
   
    Log("IV:", bc.HexFromBytes(IVB))
   
    Return IVB
       
End Sub



Sub AddIVMessage (ivb() As Byte, M() As Byte) As Byte()
   
    Log("AddIVMessage")
    Log("IV:",bc.HexFromBytes(ivb))
    Log("M:",bc.HexFromBytes(m))
   
   
    Dim IVMessageBytes (ivb.Length + M.Length) As Byte
    For i=0 To IVMessageBytes.Length-1
        IVMessageBytes(i)=0
    Next
    bc.ArrayCopy2(ivb,0,IVMessageBytes,0,16)      
    bc.ArrayCopy2(m,0,IVMessageBytes,16,M.Length)
   
    Log("IV&M",bc.HexFromBytes(IVMessageBytes))
   
    Return IVMessageBytes
End Sub

Sub AStream_Error
    Log("Connection lost. Listening for new connections...")
    server.Listen
End Sub

#if C
#include <mbedtls/aes.h>
#include "mbedtls/md.h"

char encrypted[256];
char plaintext[256];
char ivc[16];

void Encrypt (B4R::Object* o) {

char *texttoencrypt = (char*)b4r_main::_plaintext->data;
memcpy(plaintext, texttoencrypt, strlen(texttoencrypt));
char *pw = (char*)b4r_main::_pw->data;
char *ivc = (char*)b4r_main::_iv->data;


//256Hash the PW (32 Bytes)
  byte shaResult[32];
  mbedtls_md_context_t hash;
  mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
  const size_t pwLength = strlen(pw);        
  mbedtls_md_init(&hash);
  mbedtls_md_setup(&hash, mbedtls_md_info_from_type(md_type), 0);
  mbedtls_md_starts(&hash);
  mbedtls_md_update(&hash, (const unsigned char *) pw, pwLength);
  mbedtls_md_finish(&hash, shaResult);
  mbedtls_md_free(&hash);
  uint8_t key[32];
  memcpy(key, shaResult, 32);
 //***********************************************************************************
   
    uint8_t ivu[16];

//Set IV
    int i;
    for( i = 0; i < 16; i++ )
    {
    ivu[i]=ivc[i];
    //ivu[i]=i;
    }

esp_aes_context ctx;
esp_aes_init( &ctx );
esp_aes_setkey( &ctx, key, 256 );
esp_aes_crypt_cbc( &ctx, ESP_AES_ENCRYPT, sizeof(plaintext), ivu, (uint8_t*)plaintext, (uint8_t*)encrypted );

int len;
len = strlen(encrypted);
printf("Length of |%s| is |%d|\n", encrypted, len);

b4r_main::_encrypted->data=encrypted;
memset( plaintext, 0, sizeof( plaintext ) );
b4r_main::_plaintext->data=plaintext;

printf("Encrypted \n");

for( i = 0; i < 128; i++ )
    {
        printf( "%02x[%c]%c", encrypted[i], (encrypted[i]>31)?encrypted[i]:' ', ((i&0xf)!=0xf)?' ':'\n' );
    }
    printf( "\n" );

esp_aes_free( &ctx );


}

void Decrypt (B4R::Object* o) {

printf("Decrypt started \n");

char *pw = (char*)b4r_main::_pw->data;
char *ivc = (char*)b4r_main::_iv->data;

//256Hash the PW (32 Bytes)
  byte shaResult[32];
  mbedtls_md_context_t hash;
  mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
  const size_t pwLength = strlen(pw);        
  mbedtls_md_init(&hash);
  mbedtls_md_setup(&hash, mbedtls_md_info_from_type(md_type), 0);
  mbedtls_md_starts(&hash);
  mbedtls_md_update(&hash, (const unsigned char *) pw, pwLength);
  mbedtls_md_finish(&hash, shaResult);
  mbedtls_md_free(&hash);
  uint8_t key[32];
  memcpy(key, shaResult, 32);
 //***********************************************************************************
   
    uint8_t ivu[16];

printf("Hash ok\n");

//Set IV

    int i;
    for( i = 0; i < 16; i++ )
    {
    ivu[i]=ivc[i];
    //ivu[i]=i;
    }

printf("IV Set\n");

printf("IV\n");

for( i = 0; i < 16; i++ )
    {
        printf( "%02x[%c]%c", ivu[i], (ivu[i]>31)?ivu[i]:' ', ((i&0xf)!=0xf)?' ':'\n' );
    }
    printf( "\n" );

esp_aes_context ctx;
esp_aes_init( &ctx );
esp_aes_setkey( &ctx, key, 256 );


char *b4renc = (char*)b4r_main::_encrypted->data;
memcpy(encrypted, b4renc, sizeof( encrypted ));

printf("B4REnc\n");

for( i = 0; i < 128; i++ )
    {
        printf( "%02x[%c]%c", b4renc[i], (b4renc[i]>31)?b4renc[i]:' ', ((i&0xf)!=0xf)?' ':'\n' );
    }
    printf( "\n" );

printf("Encrypted after copy\n");

for( i = 0; i < 128; i++ )
    {
        printf( "%02x[%c]%c", encrypted[i], (encrypted[i]>31)?encrypted[i]:' ', ((i&0xf)!=0xf)?' ':'\n' );
    }
    printf( "\n" );


memset( plaintext, 0, sizeof( plaintext ) );

printf("Plaintext after clear\n");

for( i = 0; i < 128; i++ )
    {
        printf( "%02x[%c]%c", plaintext[i], (plaintext[i]>31)?plaintext[i]:' ', ((i&0xf)!=0xf)?' ':'\n' );
    }
    printf( "\n" );

esp_aes_crypt_cbc( &ctx, ESP_AES_DECRYPT, sizeof(encrypted), ivu, (uint8_t*)encrypted, (uint8_t*)plaintext );

printf("Plaintext after decryption\n");

for( i = 0; i < 128; i++ )
    {
        printf( "%02x[%c]%c", plaintext[i], (plaintext[i]>31)?plaintext[i]:' ', ((i&0xf)!=0xf)?' ':'\n' );
    }
    printf( "\n" );

memset( encrypted, 0, sizeof( encrypted ) );

b4r_main::_plaintext->data=plaintext;

esp_aes_free( &ctx );

}
#End if
 

Attachments

Top