[SOLVED] problem with decryption using AES

Hii All

i am making simple client server model with two Nodemcu's to send encrypted data from the client and decrypt the data at the server

here are the code that i used

server

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>

#include <ESP8266WiFiMulti.h>
ESP8266WiFiMulti WiFiMulti;

const char* ssid = "ESP8266-Access-Point";
const char* password = "123456789";


#include <Crypto.h>
#include <base64.hpp>

#define BLOCK_SIZE 16

uint8_t key[BLOCK_SIZE] = { 0x1C,0x3E,0x4B,0xAF,0x13,0x4A,0x89,0xC3,0xF3,0x87,0x4F,0xBC,0xD7,0xF3, 0x31, 0x31 };
uint8_t iv[BLOCK_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

void bufferSize(char* text, int &length)
{
  int i = strlen(text);
  int buf = round(i / BLOCK_SIZE) * BLOCK_SIZE;
  length = (buf <= i) ? buf + BLOCK_SIZE : length = buf;
}
    
void encrypt(char* plain_text, char* output, int length)
{
  byte enciphered[length];
  RNG::fill(iv, BLOCK_SIZE); 
  AES aesEncryptor(key, iv, AES::AES_MODE_128, AES::CIPHER_ENCRYPT);
  aesEncryptor.process((uint8_t*)plain_text, enciphered, length);
  int encrypted_size = sizeof(enciphered);
  char encoded[encrypted_size];
  encode_base64(enciphered, encrypted_size, (unsigned char*)encoded);
  strcpy(output, encoded);
}

void decrypt(char* enciphered, char* output, int length)
{
  length = length + 1; //re-adjust
  char decoded[length];
  decode_base64((unsigned char*)enciphered, (unsigned char*)decoded);
  bufferSize(enciphered, length);
  byte deciphered[length];
  AES aesDecryptor(key, iv, AES::AES_MODE_128, AES::CIPHER_DECRYPT);
  aesDecryptor.process((uint8_t*)decoded, deciphered, length);
  strcpy(output, (char*)deciphered);
}


//Your IP address or domain name with URL path
const char* serverNameTemp = "http://192.168.4.1/temperature";
const char* serverNameHumi = "http://192.168.4.1/humidity";
const char* serverNamePres = "http://192.168.4.1/pressure";


String temperature;
String humidity;
String pressure;

unsigned long previousMillis = 0;
const long interval = 5000; 

void setup() {
  Serial.begin(115200);
  Serial.println();
  
 
  WiFi.mode(WIFI_STA);
  WiFiMulti.addAP(ssid, password);
  while((WiFiMulti.run() == WL_CONNECTED)) { 
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("Connected to WiFi");
}

void loop() {
  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis >= interval) {
     // Check WiFi connection status
    if ((WiFiMulti.run() == WL_CONNECTED)) {
      temperature = httpGETRequest(serverNameTemp);
      humidity = httpGETRequest(serverNameHumi);
      pressure = httpGETRequest(serverNamePres);
      Serial.println(pressure );
      Serial.println(pressure.length() );

char charBuf[24];
  pressure.toCharArray(charBuf, 24);

  Serial.println(charBuf );
      Serial.println(sizeof(charBuf));
  
  char decrypted[24];
   decrypt(charBuf, decrypted, 24);



  Serial.print("Decrypted: ");
  Serial.println(decrypted);

  delay(2000);
      
      // save the last HTTP GET Request
      previousMillis = currentMillis;
    }
    else {
      Serial.println("WiFi Disconnected");
    }
  }
}

String httpGETRequest(const char* serverName) {
  WiFiClient client;
  HTTPClient http;
    
  // Your IP address with path or Domain name with URL path 
  http.begin(client, serverName);
  
  // Send HTTP POST request
  int httpResponseCode = http.GET();
  
  String payload = "--"; 
  
  if (httpResponseCode>0) {
    Serial.print("HTTP Response code: ");
    Serial.println(httpResponseCode);
    payload = http.getString();
  }
  else {
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
  }
  // Free resources
  http.end();

  return payload;
}

and for client

#include <ESP8266WiFi.h>
#include "ESPAsyncWebServer.h"

// Set your access point network credentials
const char* ssid = "ESP8266-Access-Point";
const char* password = "123456789";
char plain_text[] = "1234";


// Create AsyncWebServer object on port 80
AsyncWebServer server(80);


#include <Crypto.h>
#include <base64.hpp>

#define BLOCK_SIZE 16

uint8_t key[BLOCK_SIZE] = { 0x1C,0x3E,0x4B,0xAF,0x13,0x4A,0x89,0xC3,0xF3,0x87,0x4F,0xBC,0xD7,0xF3, 0x31, 0x31 };
uint8_t iv[BLOCK_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

void bufferSize(char* text, int &length)
{
  int i = strlen(text);
  int buf = round(i / BLOCK_SIZE) * BLOCK_SIZE;
  length = (buf <= i) ? buf + BLOCK_SIZE : length = buf;
}

char encrypted[16];
    
void encrypt(char* plain_text, char* output, int length)
{
  byte enciphered[length];
  RNG::fill(iv, BLOCK_SIZE); 
  AES aesEncryptor(key, iv, AES::AES_MODE_128, AES::CIPHER_ENCRYPT);
  aesEncryptor.process((uint8_t*)plain_text, enciphered, length);
  int encrypted_size = sizeof(enciphered);
  char encoded[encrypted_size];
  encode_base64(enciphered, encrypted_size, (unsigned char*)encoded);
  strcpy(output, encoded);
}

void decrypt(char* enciphered, char* output, int length)
{
  length = length + 1; //re-adjust
  char decoded[length];
  decode_base64((unsigned char*)enciphered, (unsigned char*)decoded);
  bufferSize(enciphered, length);
  byte deciphered[length];
  AES aesDecryptor(key, iv, AES::AES_MODE_128, AES::CIPHER_DECRYPT);
  aesDecryptor.process((uint8_t*)decoded, deciphered, length);
  strcpy(output, (char*)deciphered);
}




void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);
  Serial.println();
  
  // Setting the ESP as an access point
  Serial.print("Setting AP (Access Point)…");
  // Remove the password parameter, if you want the AP (Access Point) to be open
  WiFi.softAP(ssid, password);

  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);

  
}
 
void loop(){
  
  encrypt(plain_text, encrypted, 16);
  Serial.println(encrypted);
  delay(1000);

  
  server.on("/pressure", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", encrypted);
  });
  
  bool status;

  // Start server
  server.begin();
}

i could receive the encrypted data on the server ,but when i used the decryption function i failed to get back the enciphered text to its orignal plain text

any idea how what is wrong with the code?

thx in advance for ur help

How is the IV handled on decryption?

For a start it looks to me like your encrypt/decrypt routines assume the base64 encoding is the same length as the raw data. That's not correct. Base64 encodes 6 bits per character, so the base64 encoding will be longer by about 33%.

Edit: And yes, you need the same IV on the decrypt end as you used during encryption.

MarkT:
How is the IV handled on decryption?

i tried the decryption to be made on the same nodemcu for the encrypted message and it is working fine,but when it is on the server it never works

i am not the creator for the library ,it is crypto library that has already the functions of encryption and decryption as well as IV

pcbbc:
For a start it looks to me like your encrypt/decrypt routines assume the base64 encoding is the same length as the raw data. That's not correct. Base64 encodes 6 bits per character, so the base64 encoding will be longer by about 33%.

Edit: And yes, you need the same IV on the decrypt end as you used during encryption.

Hello pcbbc

i tried the decryption to be made on the same nodemcu for the encrypted message and it is working fine,but when it is on the server it never works

i am not the creator for the library ,it is crypto library that has already the functions of encryption and decryption as well as IV

In this type of encryption, a single key is used for encryption and decryption. It is faster than it's counterpart: asymmetric encryption mystarbucksvisit. But it also has some drawbacks. For example, a single key is used for encryption and decryption, so when you encrypt the date, then you have to provide the same key for decryption Mystarbucksvisit

mestek86:
i tried the decryption to be made on the same nodemcu for the encrypted message and it is working fine,but when it is on the server it never works

i am not the creator for the library ,it is crypto library that has already the functions of encryption and decryption as well as IV

Because the IV is all zero's at the decryption end and not initialized with the same values as at the source.
Commend out this line...

RNG::fill(iv, BLOCK_SIZE);

It won't be as secure, but it will work.

To get it to work with an IV you need to also send the IV to the destination. When you run the encryption locally the IV is set by the encrypt and remains in place for the decrypt. When you run the decrypt remotely the IV is all zeros (because that's what it is initialized to).

Also your code only "works" because you are encrypting a 4 character string with a null terminator and through sheer blind chance none of your buffer overruns cause an error.

You are encoding 16 bytes as base64 which will produce a 24 character string (25 with null terminator) but have only allocated a 16 byte buffer for it:

 byte enciphered[length];
...
  int encrypted_size = sizeof(enciphered); //same as length
  char encoded[encrypted_size];
  encode_base64(enciphered, encrypted_size, (unsigned char*)encoded);
  strcpy(output, encoded);

Try...

char encrypted[25];

void encrypt(char* plain_text, char* output, int length)
{
  byte enciphered[length];
  //RNG::fill(iv, BLOCK_SIZE);
  AES aesEncryptor(key, iv, AES::AES_MODE_128, AES::CIPHER_ENCRYPT);
  aesEncryptor.process((uint8_t*)plain_text, enciphered, length);
  int encrypted_size = ((length * 8 + 23) / 24) * 4 + 1;
  char encoded[encrypted_size];
  encode_base64(enciphered, length, (unsigned char*)encoded);
  strcpy(output, encoded);
}

Your decrypt is similarly broken but I haven't fixed it.

The question has already been posted twice. OP has got numerous answers but not minded those. OP will only accept a cut'n paste sollution to the problem.

Danois90:
The question has already been posted twice. OP has got numerous answers but not minded those. OP will only accept a cut'n paste sollution to the problem.

That's a pity. I'm here to help people learn not provide cut and paste solutions... I think I wean quite far enough trying to fix their encrypt code.

I'd also have though it would be far easier to use https, which seems to be perfectly possible with NodeMCU/ESP8266. Then there's no need to encrypt the payload separately.

Danois90:
The question has already been posted twice. OP has got numerous answers but not minded those. OP will only accept a cut'n paste sollution to the problem.

it is a totally different code ,and u r replies r not helpful,i think u need to be more friendly and at least let others share discussion and ideas

pcbbc:
That's a pity. I'm here to help people learn not provide cut and paste solutions... I think I wean quite far enough trying to fix their encrypt code.

I'd also have though it would be far easier to use https, which seems to be perfectly possible with NodeMCU/ESP8266. Then there's no need to encrypt the payload separately.

Hello pcbbc

thank u very much for ur help ,i am really working on ur suggestions and will show the results to be helpful for others that may need a similar topic in the future

for others (Danois90) that make assumptions about ppl and assuming that they r copying and pasting codes ,i think they have to revise the topics that they think it is repeated

pcbbc:
Because the IV is all zero's at the decryption end and not initialized with the same values as at the source.
Commend out this line...

RNG::fill(iv, BLOCK_SIZE);

It won't be as secure, but it will work.

To get it to work with an IV you need to also send the IV to the destination. When you run the encryption locally the IV is set by the encrypt and remains in place for the decrypt. When you run the decrypt remotely the IV is all zeros (because that's what it is initialized to).

Also your code only "works" because you are encrypting a 4 character string with a null terminator and through sheer blind chance none of your buffer overruns cause an error.

very good point

pcbbc:
That's a pity. I'm here to help people learn not provide cut and paste solutions... I think I wean quite far enough trying to fix their encrypt code.

I'd also have though it would be far easier to use https, which seems to be perfectly possible with NodeMCU/ESP8266. Then there's no need to encrypt the payload separately.

the reason of using encryption/decryption is for educational purpose in IoT ,and yes u r true https is simpler and i have already discussed it with my group

thx for that suggestion and for being very helpful with constructive comments and inputs

Try...

char encrypted[25];

void encrypt(char* plain_text, char* output, int length)
{
  byte enciphered[length];
  //RNG::fill(iv, BLOCK_SIZE);
  AES aesEncryptor(key, iv, AES::AES_MODE_128, AES::CIPHER_ENCRYPT);
  aesEncryptor.process((uint8_t*)plain_text, enciphered, length);
  int encrypted_size = ((length * 8 + 23) / 24) * 4 + 1;
  char encoded[encrypted_size];
  encode_base64(enciphered, length, (unsigned char*)encoded);
  strcpy(output, encoded);
}

Your decrypt is similarly broken but I haven't fixed it.
[/quote]

Is working like charm ,thank u very much
will add both sketches after modifications to be availabel for future mates
many thx

working codes

transmitter(client)

#include <ESP8266WiFi.h>
#include "ESPAsyncWebServer.h"

// Set your access point network credentials
const char* ssid = "ESP8266-Access-Point";
const char* password = "123456789";
char plain_text[] = "1234";


// Create AsyncWebServer object on port 80
AsyncWebServer server(80);


#include <Crypto.h>
#include <base64.hpp>

#define BLOCK_SIZE 16

uint8_t key[BLOCK_SIZE] = { 0x1C,0x3E,0x4B,0xAF,0x13,0x4A,0x89,0xC3,0xF3,0x87,0x4F,0xBC,0xD7,0xF3, 0x31, 0x31 };
uint8_t iv[BLOCK_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

void bufferSize(char* text, int &length)
{
  int i = strlen(text);
  int buf = round(i / BLOCK_SIZE) * BLOCK_SIZE;
  length = (buf <= i) ? buf + BLOCK_SIZE : length = buf;
}

char encrypted[25];

void encrypt(char* plain_text, char* output, int length)
{
  byte enciphered[length];
  //RNG::fill(iv, BLOCK_SIZE);
  AES aesEncryptor(key, iv, AES::AES_MODE_128, AES::CIPHER_ENCRYPT);
  aesEncryptor.process((uint8_t*)plain_text, enciphered, length);
  int encrypted_size = ((length * 8 + 23) / 24) * 4 + 1;
  char encoded[encrypted_size];
  encode_base64(enciphered, length, (unsigned char*)encoded);
  strcpy(output, encoded);
}

void decrypt(char* enciphered, char* output, int length)
{
  length = length + 1; //re-adjust
  char decoded[length];
  decode_base64((unsigned char*)enciphered, (unsigned char*)decoded);
  bufferSize(enciphered, length);
  byte deciphered[length];
  AES aesDecryptor(key, iv, AES::AES_MODE_128, AES::CIPHER_DECRYPT);
  aesDecryptor.process((uint8_t*)decoded, deciphered, length);
  strcpy(output, (char*)deciphered);
}




void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);
  Serial.println();
  
  // Setting the ESP as an access point
  Serial.print("Setting AP (Access Point)…");
  // Remove the password parameter, if you want the AP (Access Point) to be open
  WiFi.softAP(ssid, password);

  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);

  
}
 
void loop(){
  
  encrypt(plain_text, encrypted, 16);
  Serial.println(encrypted);
  delay(1000);

  
  server.on("/pressure", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", encrypted);
  });
  
  bool status;

  // Start server
  server.begin();
}

receiver code (server)

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>

#include <ESP8266WiFiMulti.h>
ESP8266WiFiMulti WiFiMulti;

const char* ssid = "ESP8266-Access-Point";
const char* password = "123456789";


#include <Crypto.h>
#include <base64.hpp>

#define BLOCK_SIZE 16

uint8_t key[BLOCK_SIZE] = { 0x1C,0x3E,0x4B,0xAF,0x13,0x4A,0x89,0xC3,0xF3,0x87,0x4F,0xBC,0xD7,0xF3, 0x31, 0x31 };
uint8_t iv[BLOCK_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

void bufferSize(char* text, int &length)
{
  int i = strlen(text);
  int buf = round(i / BLOCK_SIZE) * BLOCK_SIZE;
  length = (buf <= i) ? buf + BLOCK_SIZE : length = buf;
}
    
void encrypt(char* plain_text, char* output, int length)
{
  byte enciphered[length];
  RNG::fill(iv, BLOCK_SIZE); 
  AES aesEncryptor(key, iv, AES::AES_MODE_128, AES::CIPHER_ENCRYPT);
  aesEncryptor.process((uint8_t*)plain_text, enciphered, length);
  int encrypted_size = sizeof(enciphered);
  char encoded[encrypted_size];
  encode_base64(enciphered, encrypted_size, (unsigned char*)encoded);
  strcpy(output, encoded);
}

void decrypt(char* enciphered, char* output, int length)
{
  length = length + 1; //re-adjust
  char decoded[length];
  decode_base64((unsigned char*)enciphered, (unsigned char*)decoded);
  bufferSize(enciphered, length);
  byte deciphered[length];
  AES aesDecryptor(key, iv, AES::AES_MODE_128, AES::CIPHER_DECRYPT);
  aesDecryptor.process((uint8_t*)decoded, deciphered, length);
  strcpy(output, (char*)deciphered);
}


//Your IP address or domain name with URL path
const char* serverNameTemp = "http://192.168.4.1/temperature";
const char* serverNameHumi = "http://192.168.4.1/humidity";
const char* serverNamePres = "http://192.168.4.1/pressure";


String temperature;
String humidity;
String pressure;

unsigned long previousMillis = 0;
const long interval = 5000; 

void setup() {
  Serial.begin(115200);
  Serial.println();
  
 
  WiFi.mode(WIFI_STA);
  WiFiMulti.addAP(ssid, password);
  while((WiFiMulti.run() == WL_CONNECTED)) { 
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("Connected to WiFi");
}

void loop() {
  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis >= interval) {
     // Check WiFi connection status
    if ((WiFiMulti.run() == WL_CONNECTED)) {
      temperature = httpGETRequest(serverNameTemp);
      humidity = httpGETRequest(serverNameHumi);
      pressure = httpGETRequest(serverNamePres);
      Serial.println(pressure );
      Serial.println(pressure.length() );

char charBuf[24];
  pressure.toCharArray(charBuf, 24);

  Serial.println(charBuf );
      Serial.println(sizeof(charBuf));
  
  char decrypted[24];
   decrypt(charBuf, decrypted, 24);



  Serial.print("Decrypted: ");
  Serial.println(decrypted);

  delay(2000);
      
      // save the last HTTP GET Request
      previousMillis = currentMillis;
    }
    else {
      Serial.println("WiFi Disconnected");
    }
  }
}

String httpGETRequest(const char* serverName) {
  WiFiClient client;
  HTTPClient http;
    
  // Your IP address with path or Domain name with URL path 
  http.begin(client, serverName);
  
  // Send HTTP POST request
  int httpResponseCode = http.GET();
  
  String payload = "--"; 
  
  if (httpResponseCode>0) {
    Serial.print("HTTP Response code: ");
    Serial.println(httpResponseCode);
    payload = http.getString();
  }
  else {
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
  }
  // Free resources
  http.end();

  return payload;
}

special thanks to the genuis pcbbc

Sollution was a cut'n pasted one, as said. Transmitter will cause problems, buffer overflow..

Danois90:
Sollution was a cut'n pasted one, as said. Transmitter will cause problems, buffer overflow..

i duno why u r not encourging ppl to learn,i have written in the thread it is for educational purpose

How about some real words, instead of whatever you are typing? It's not like there is a shortage of bandwidth anymore.

CrossRoads:
How about some real words, instead of whatever you are typing? It's not like there is a shortage of bandwidth anymore.

hello dear

it is my pleasure to read your comment here,it is not related to band width ,it is just related to fast typing

Your still decrypting 32 bytes at the destination when you are only sending 16 because you've confused the base 64 encoded length (24) with the raw encrypted data length (16).

Although no buffer overruns this time, at least as far as I can see.

  1. Your decrypt routine doesn't need a length passing in. You can work that out from strlen(enciphered) and the fact that each 4 characters of a base64 string encode 3 bytes.
  2. decode_base64 returns the number of bytes actually decoded. You can use that for the length for aesDecryptor.process.
  3. Assuming the transmitter always sends multiples of 16 bytes there's no need for the bufferSize function either.