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:
B4R-Code (change the SSID and PW). The B4J project is attached. .
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:
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