This post shows how you might send encrypted data over as serial communications port. The example was written for the MSP432 using the Energia environment. It uses AES128 ECB encryption using a pre-shared, hard coded (bad practice) key. The example generates a simple text message with a counter that counts from 0 to 255 repeatedly. The message is encrypted and sent as a HEX string (so I can read it) over a serial port. The receiver is a python script that decrypts the data and displays it to the screen. The code was developed in a Linux environment but should work fine in other operating systems – you will need to change the path for the serial port.
This example does not deal with the thorny issue of key distribution.
Energia code
#include <msp432.h>
#include <stdio.h>
/* Serial communications with AES 128 ECB encryption
Data sent in ASCII hex (e.g. the value 0x02 will
be sent as the string "02". Transmitting in this
way allows us monitor the messages using a simple
serial monitor. It also allows us use the
Serial.print and Serial.println methods without
worrying about data values that are zero (which
will be interpreted as end of string markers by the
Serial library routines. Of course this is less
efficient but it will hopefully be instructive.
*/
// Data block size (in bytes)
#define BLOCKSIZE 16
const char key[] = "8d2e60365f17c7df1040d7501b4a7b5a";
char plaintext[BLOCKSIZE];
///const char MSG[] = "Mary had a little lamb"; // "Mary had a little lamb";
// the setup routine runs once when you press reset:
void setup() {
Serial.begin(38400);
}
int Counter;
// the loop routine runs over and over again forever:
void loop() {
sprintf(plaintext,"Counter=%d",Counter);
Counter++;
if (Counter > 255)
Counter = 0;
SendEncryptedMessage(plaintext, sizeof(plaintext) );
delay(1000);
}
uint32_t ByteStringToNumber(const char *Str)
{
uint32_t UpperNibble = 0;
uint32_t LowerNibble = 0;
if (Str[0] > '9')
UpperNibble = ( Str[0] | 32 ) - 'a' + 10; // ensure lower case, remove hex 'a' and offset by 10
else
UpperNibble = ( Str[0] - '0');
if (Str[1] > '9')
LowerNibble = ( Str[1] | 32 ) - 'a' + 10; // ensure lower case, remove hex 'a' and offset by 10
else
LowerNibble = ( Str[1] - '0');
return (UpperNibble << 4) + LowerNibble;
}
void setKey(const char * keystr)
{
// The key will take the form of a 32 character ASCII string produced
// by a tool like openssl.
// This routine will extract the bytes from the string and store them
// in the AES accelerator
int Index;
uint32_t KeySection;
uint8_t *Ptr = (uint8_t *)(&AESAKEY);
for (Index = 0; Index < 16; Index++)
{
KeySection = ByteStringToNumber(keystr + Index * 2);
*Ptr = (uint8_t) KeySection;
}
}
void SendEncryptedMessage(const char *Payload, unsigned int len)
{
// Send the payload over the encrypted channel
// Message is padded with zeros if it is not a multiple of BLOCKSIZE
unsigned int BlockIndex = 0;
unsigned int TotalByteCount = 0;
uint8_t PlainTextBuffer[BLOCKSIZE];
uint8_t CryptoText[BLOCKSIZE];
volatile uint8_t *InputDataRegister;
volatile uint8_t *OutputDataRegister;
AESACTL0 = 0; // Set the AES engine into encryption mode
setKey(key); // Must set key after changing mode
while (TotalByteCount < len)
{
BlockIndex = 0;
while (BlockIndex < BLOCKSIZE)
{
if (TotalByteCount < len)
PlainTextBuffer[BlockIndex] = Payload[TotalByteCount];
else
PlainTextBuffer[BlockIndex] = 0;
BlockIndex++;
TotalByteCount++;
}
InputDataRegister = (uint8_t *)&AESADIN;
for (BlockIndex = 0; BlockIndex < BLOCKSIZE; BlockIndex++)
*InputDataRegister = PlainTextBuffer[BlockIndex];
delay(1);
OutputDataRegister = (uint8_t *)&AESADOUT;
for (BlockIndex = 0; BlockIndex < 16; BlockIndex++)
CryptoText[BlockIndex] = *OutputDataRegister;
for (BlockIndex = 0; BlockIndex < BLOCKSIZE; BlockIndex++)
{
if (CryptoText[BlockIndex] < 16)
Serial.print("0"); // send leading zeros for values < 16
Serial.print(int(CryptoText[BlockIndex]), HEX);
}
Serial.println(""); // Send a line feed after each block
}
}
Python code
# This python script receives encrypted data over the serial port
# The encryption method is AES128, ECB and the key is hardcoded into
# sender and receiver
# Expected received data format:
# ASCII-HEX 32 characters (representing 16 bytes or 128 bits) followed by
# CR LF (\r\n)
from Crypto.Cipher import AES
import serial as ser;
import binascii
port=ser.serial_for_url("/dev/ttyACM0") # CHANGE THIS TO SUIT YOUR SITUATION!!!
port.baudrate=38400
key = binascii.unhexlify('8d2e60365f17c7df1040d7501b4a7b5a')
IV = 16 * '\x00' # set inital vector (or state) to 0
mode = AES.MODE_ECB # Electronic Code Book encryption used
encryptor = AES.new(key, mode, IV=IV)
while (1):
ciphertext=port.read_until()
if len(ciphertext)==34: # got a full packet?
ciphertext=ciphertext[:32] # trim off CR LF
# convert to actual values rather than hex string
ciphertext=binascii.unhexlify(ciphertext)
plaintext = encryptor.decrypt(ciphertext) # decrypt
print(plaintext)