AES CBC encrypt and decrypt

Hi All,

I am working on creating a fuction which I can pass an encrypted string to and then have it decrypt and print the data to the serial line. This all seemed pretty easy following the guidelines of @zachary’s demo here:

I have setup a test application which just encrypts a string and then passes it to my test function who’s only job is to decrypt it and print it to the serial line. Here is the code:

/* Includes ------------------------------------------------------------------*/  
#include "application.h"
#include "spark_protocol.h"
#include "tropicssl/rsa.h"
#include "tropicssl/aes.h"
#include "spark_utilities.h"

//Global variables
unsigned char key[16] = {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01};
unsigned char iv[16] = {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01};
aes_context aes;

//Fucntions
void cbcDecrypt(String messageBody);
size_t pad(unsigned char *buf, size_t messageLength);

SYSTEM_MODE(MANUAL);

void setup(){
	Serial.begin(9600);
}

void loop(){


	const char *sTest = "Well isn't this just fantastic that it works.";
	int length = strlen(sTest) + 1;

	unsigned char buf[64];
	memcpy(buf, sTest, length);

	size_t paddedLength = pad(buf, length);

	aes_setkey_enc(&aes, key, 128);
	aes_crypt_cbc(&aes, AES_ENCRYPT, paddedLength, iv, buf, buf);

	cbcDecrypt((const char*)buf);

	delay(10000);
}

void cbcDecrypt(String messageBody){
	const char *message = messageBody.c_str();
	int length = strlen(message);

	//Create input and output buffers for encryption.
	unsigned char buf[length];
	unsigned char oBuf[strlen(message)];

	//Make sure buffers are empy
	memset(buf, 0, length);
	memset(oBuf, 0, strlen(message));

	//Copy message into input buffer
	memcpy(buf, message, length);

	size_t paddedLength = pad(buf, length);

	//Decrypt message into output buffer
	aes_setkey_dec(&aes, key, 128);
	aes_crypt_cbc(&aes, AES_DECRYPT, paddedLength, iv, buf, oBuf);

	//Store first 16 decrypted characters as round key for next cbc encrypted send
	for(int i = 0; i < 16; i++){
		EEPROM.write(i+36, oBuf[i]);
	}

	//Print out the decrypted message and the length of the encrypted message received
	Serial.print("Decrypted return: ");
	Serial.println((char *)oBuf);
	Serial.print("Return length+1 = ");
	Serial.println(length);
}

size_t pad(unsigned char *buf, size_t messageLength) {
  size_t paddedLength = (messageLength & ~15) + 16;
  char pad = paddedLength - messageLength;
  memset(buf + messageLength, pad, pad);
  return paddedLength;
}

Here is the output I am getting in my log:

Decrypted return: <ðó¯)ÕL ­Xeyӏjust fantastic that it works.
Return length+1 = 50
Decrypted return: ïéñ–VgÊ«ª:ßjust fantastic that it works.
Return length+1 = 50
Decrypted return: 8?uº`hþ±ìÐøՀijust fantastic that it works.
Return length+1 = 50

What in the world is going on here? Casting problem?

Thank you,
Travis Elliott

Since the first 16 bytes is the IV for the next round of decryption, you don't want to print these.

This statement prints the string from the beginning, including the 16 IV bytes:

If you change it to

Serial.println(oBuf+16);

That will skip the first 16 bytes, leaving just the decrypted message.

BTW, I would avoid using strlen() on the encrypted message, since you may have a null byte in there which would cause the message to appear much shorter than it actually is.

1 Like

Thank you @mdma

Serial.println(oBuf+16); throws an exception:
call of overloaded 'println(unsigned char*)' is ambiguous

Also I did not realize the iv was prepended to the data returned from AES cbc decrypt. That did not seem to be the case in @zachary's example. Are you sure on this?

Hi @mdma,

I tried your suggestion which just chops off the front part of the message. This is what my log looks like now:

Decrypted return: just fantastic that it works.
Return length+1 = 50
Decrypted return: just fantastic that it works.
Return length+1 = 50
Decrypted return: just fantastic that it works.
Return length+1 = 50

The first part which is "Well isn’t this " is getting cut off. This is exactly 16 characters so when I do oBuf+16 it just chops it off. It still is not being decrypted correctly.

Ah sorry, I misread the code - I thought the IV was being returned in the first 16 chars because you store it in the EEPROM, but of course that’s just the first 16 chars of the message. (It’s late here, I should know better than to start tackling hard things late at night!)

I’ll take a look again tomorrow if no-one else has figured it out before then! :smile:

No problem @mdma I will look further into the tropicssl documentation. Hopefully someone will have an idea what in the world Im doing wrong. I am almost sure it is something simple :smile:

I couldn't put it down! :slight_smile: I looked over the demo - one key difference is that you may need to reinitialize iv, since that's changed by the aes_crypt_cbc() function. The simplest way to do that is to create a new 16 byte buffer, and copy your original iv to that, and pass that to aes_crypt_cbc(), and do that for both encryption and decryption.

Also check the buf[64] - maybe change to 128 to be sure the buffer is large enough.

1 Like

WHOOO that worked @mdma, well mostly :smile: I only had one return which was funked. Maybe it is from using strlen like you said? What should I use instead?

I did not have to adjust the size of buf. I just made a new global variable originaliv and copied my iv into that and used that for decrypt.

Here is the new code which seems to be working(mostly :smile:)!

/* Includes ------------------------------------------------------------------*/  
#include "application.h"
#include "spark_protocol.h"
#include "tropicssl/rsa.h"
#include "tropicssl/aes.h"
#include "spark_utilities.h"

//Global variables
unsigned char key[16] = {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01};
unsigned char iv[16];
unsigned char origionaliv[16];
aes_context aes;

//Fucntions
void cbcDecrypt(String messageBody);
size_t pad(unsigned char *buf, size_t messageLength);
void sixteenRandomBytes(unsigned char buf[16]);

SYSTEM_MODE(MANUAL);

void setup(){
	Serial.begin(9600);
}

void loop(){


	const char *sTest = "Well isn't this just fantastic that it works.";
	int length = strlen(sTest) + 1;

	unsigned char buf[64];
	memcpy(buf, sTest, length);

	size_t paddedLength = pad(buf, length);

	sixteenRandomBytes(iv);
	memcpy(origionaliv, iv, 16);

	aes_setkey_enc(&aes, key, 128);
	aes_crypt_cbc(&aes, AES_ENCRYPT, paddedLength, iv, buf, buf);

	cbcDecrypt((const char*)buf);

	delay(10000);
}

void cbcDecrypt(String messageBody){
	const char *message = messageBody.c_str();
	int length = strlen(message);

	//Create input and output buffers for encryption.
	unsigned char buf[length];
	unsigned char oBuf[strlen(message)];

	//Make sure buffers are empy
	memset(buf, 0, length);
	memset(oBuf, 0, strlen(message));

	//Copy message into input buffer
	memcpy(buf, message, length);

	size_t paddedLength = pad(buf, length);

	//Decrypt message into output buffer
	aes_setkey_dec(&aes, key, 128);
	aes_crypt_cbc(&aes, AES_DECRYPT, paddedLength, origionaliv, buf, oBuf);

	//Store first 16 decrypted characters as round key for next cbc encrypted send
	for(int i = 0; i < 16; i++){
		EEPROM.write(i+36, oBuf[i]);
	}

	//Print out the decrypted message and the length of the encrypted message received
	Serial.print("Decrypted return: ");
	Serial.println((char *)oBuf);
	Serial.print("Return length+1 = ");
	Serial.println(length);
}

size_t pad(unsigned char *buf, size_t messageLength) {
  size_t paddedLength = (messageLength & ~15) + 16;
  char pad = paddedLength - messageLength;
  memset(buf + messageLength, pad, pad);
  return paddedLength;
}

void sixteenRandomBytes(unsigned char buf[16]) {
  for (int i = 0; i < 16; i++) {
    buf[i] = rand() & 0xff;
  }
}

I would change cbcDecrypt to take a uint8_t* data, size_t len i.e.

void cbcDecrypt(uint8_t* data, size_t len);
``

So you explicitly pass the length, since null termination (as done with strings) isn't possible with binary data, where null may be a valid part of the data.
1 Like

Thank you so much @mdma seems to be perfect now. Now I just have to apply this to my real application which will be passing an encrypted string returned from my PHP server via HTTP Get. Will probably be asking questions about that soon enough :wink: Here is the completed code which has been running flawlessly for about 10 minutes now

/* Includes ------------------------------------------------------------------*/  
#include "application.h"
#include "spark_protocol.h"
#include "tropicssl/rsa.h"
#include "tropicssl/aes.h"
#include "spark_utilities.h"

//Global variables
unsigned char key[16] = {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01};
unsigned char iv[16];
unsigned char origionaliv[16];
aes_context aes;

//Fucntions
void cbcDecrypt(uint8_t* data, size_t len);
size_t pad(unsigned char *buf, size_t messageLength);
void sixteenRandomBytes(unsigned char buf[16]);

SYSTEM_MODE(MANUAL);

void setup(){
	Serial.begin(9600);
}

void loop(){


	const char *sTest = "Well isn't this just fantastic that it works.";
	int length = strlen(sTest) + 1;

	unsigned char buf[64];
	memcpy(buf, sTest, length);

	size_t paddedLength = pad(buf, length);

	sixteenRandomBytes(iv);
	memcpy(origionaliv, iv, 16);

	aes_setkey_enc(&aes, key, 128);
	aes_crypt_cbc(&aes, AES_ENCRYPT, paddedLength, iv, buf, buf);

	cbcDecrypt(buf, sizeof buf);

	delay(10000);
}

void cbcDecrypt(uint8_t* data, size_t len){
//	const char *message = messageBody.c_str();
//	int length = strlen(message);

	//Create input and output buffers for encryption.
	unsigned char buf[len];
	unsigned char oBuf[len];

	//Make sure buffers are empy
	memset(buf, 0, len);
	memset(oBuf, 0, len);

	//Copy message into input buffer
	memcpy(buf, data, len);

	size_t paddedLength = pad(buf, len);

	//Decrypt message into output buffer
	aes_setkey_dec(&aes, key, 128);
	aes_crypt_cbc(&aes, AES_DECRYPT, paddedLength, origionaliv, buf, oBuf);

	//Store first 16 decrypted characters as round key for next cbc encrypted send
	for(int i = 0; i < 16; i++){
		EEPROM.write(i+36, oBuf[i]);
	}

	//Print out the decrypted message and the length of the encrypted message received
	Serial.print("Decrypted return: ");
	Serial.println((char *)oBuf);
	Serial.print("Return length+1 = ");
	Serial.println(len);
}

size_t pad(unsigned char *buf, size_t messageLength) {
  size_t paddedLength = (messageLength & ~15) + 16;
  char pad = paddedLength - messageLength;
  memset(buf + messageLength, pad, pad);
  return paddedLength;
}

void sixteenRandomBytes(unsigned char buf[16]) {
  for (int i = 0; i < 16; i++) {
    buf[i] = rand() & 0xff;
  }
}
2 Likes

Wonderful! Glad to hear it’s working! :+1:

Hey @mdma and @zachary,

Would the two of you mind sanity checking my latest function for cbcDecrypt here:

void cbcDecrypt(uint8_t* data, size_t len){

	unsigned char decryptIV[16];

	for(int i = 0; i < 16; i++){
		decryptIV[i] = EEPROM.read(i+20);
	}

	Serial.print("Data prior to decrypt: ");
	hexPrint(data, len);

	Serial.print("Decrypt IV: ");
	hexPrint(decryptIV, sizeof decryptIV);
	Serial.print("Decrypt KEY: ");
	hexPrint(key, sizeof key);

	//Create input and output buffers for encryption.
	unsigned char buf[len];
	unsigned char oBuf[len];

	//Make sure buffers are empty
	memset(buf, 0, len);
	memset(oBuf, 0, len);

	//Copy message into input buffer
	memcpy(buf, data, len);

	size_t paddedLength = pad(buf, len);



	//Decrypt message into output buffer
	aes_setkey_dec(&aes, key, 128);
	aes_crypt_cbc(&aes, AES_DECRYPT, paddedLength, decryptIV, buf, oBuf);

	//Store first 16 decrypted characters as round key for next cbc encrypted send
	for(int i = 0; i < 16; i++){
		EEPROM.write(i+36, oBuf[i]);
	}

	//Print out the decrypted message and the length of the encrypted message received
	delay(50);
	Serial.print("Decrypted: ");
	Serial.println((const char *)oBuf);
	Serial.print("Decrypted HEX: ");
	hexPrint(oBuf, sizeof oBuf);
}

As you can see here i print the IV and KEY being used to decrypt in hex format, as well as the encrypted data and the decrypted result. All are correct except for the decrypted result. I have a Java Test server which is sending this encrypted data to the board as a reply. On the server I encrypt the data “Hello” and send it to the board. After sending on the server I go to a function to decrypt which also prints out the IV, KEY, Encrypted data, and Decrypted result in hex format just as I do on the spark. Decryption passes on the java server printing out the result as Hello or hex 48 65 6C 6C 6F. Any idea on what could be wrong with my cbcDecrypt on the spark? Here is the log print from the spark:

Data prior to decrypt: 92 AF B9 69 4A BB 6A CB 8A 59 8B EF 0E 82 97 70
Decrypt IV: 30 38 30 30 32 38 35 61 33 65 65 62 46 50 4F 52
Decrypt KEY: 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
Decrypted: hMLLM#.z(~~y]KTIòò5ˆT¡²7CÓ)’¯¹iJ»jˊY‹‚—p
Decrypted HEX: 68 4D 4C 4C 4D 23 2E 7A 28 7E 7E 79 5D 4B 54 49

The only thing I can see is the Java Cipher library does not support PKCS7 Padding, only PKCS5 padding which all reading on the web reassures me this is not an issue since they are the same. Do you concur on this?

Also the Java Cipher Library really does not specify if it is using AES 128 or AES 256 but users seem to be using it with 128 so I assume it works.

Thank you,
Travis Elliott

I can verify that openssl decrypts your example data, IV, and key to Hello:

openssl aes-128-cbc -d -in ciphertext \
  -iv 30383030323835613365656246504F52 \
  -K 0102030405060708090A0B0C0D0E0F10 | xxd
0000000: 4865 6c6c 6f                             Hello

This stackexchange answer verifies that, as far as you are concerned, PKCS#5 and PKCS#7 are the same.

That means the firmware is the culprit, so here are the things I notice at a glance:

  • You are not using padding correctly. The line size_t paddedLength = pad(buf, len); is acting on the ciphertext. Ciphertext in AES 128 is always 16 bytes. Padding is added only to plaintext messages before encrypting them. You should remove that line.
  • That may be the only problem, but if you still have problems, I know that we always use the same buffer for input and output. I’m also pretty sure that tropicssl modifies its internal IV buffer with each block. This is how tropicssl is intended to be used. Having separate buffers shouldn’t be a problem, but it’s something to try. Our decrypt for reference.
1 Like

Hi @zachary

I hope you had a nice vacation.

This code is now running reliably for me. Just pass it an encrypted message and it decrypts. Optionally an IV and Key could be passed for decryption but my Key is static and the IV is stored in memory when exchanges take place appropriately.

Also I almost have the cbcEncrypt function written the way I want. Trying to figure out how to properly return arrays from functions in C++. I was mainly a Java, Python, PHP, C# guy so this low level C++ stuff is new to me.

void cbcDecrypt(uint8_t* data, size_t len){
	//get round key for encryption
	unsigned char decryptIV[16];

	//Get round key out of memory for decrypt
	for(int i = 0; i < 16; i++){
		decryptIV[i] = EEPROM.read(i+20);
	}

	//Create input/output buffer for decryption.
	unsigned char buf[len];

	//Copy message into decrypt buffer
	memcpy(buf, data, len);

	//Decrypt message into output buffer
	aes_setkey_dec(&aes, key, 128);
	aes_crypt_cbc(&aes, AES_DECRYPT, sizeof buf, decryptIV, buf, buf);

	//Store first 16 decrypted characters as round key for next cbc encrypted send
	for(int i = 0; i < 16; i++){
		EEPROM.write(i+20, buf[i]);
	}

	//Extrapolate message from received data. Strings should always end with a ~ character as an end character
	String message = (const char*)buf;
	int end = message.indexOf("~");
	String trimmed = message.substring(0,end);

	//Print out the decrypted message and the length of the encrypted message received
	delay(50);
	Serial.print("Decrypted: ");
	Serial.println(trimmed);
}
1 Like

How did you do that) I still having troubles in compiling. Added tropicss to project but still no luck to compile (tropicssl/rsa.h not found)

Eh…That was long long ago in a galaxy far far away for sure. I do have a github repo for that project here though, perhaps that will help:

I couldn’t even tell you for certain if this was built using a Spark Core or a Photon to be honest.

1 Like