Sending encrypted data from an MSP432 to a python script

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) 

Energia and the MSP432 AES encryption engine

I’ve been trying to get matching results between the TI-MSP432’s encryption engine and a python equivalent on my laptop. It has been a bit frustrating but I think I’ve got it. The main cause of my frustration is that the AES registers are declared as if they represented 16 bit quantities and so the compiler was generating 2 byte writes to the registers. For example, the AESAKEY register is declared in msp432p401r_classic.h as follows:

#define AESAKEY (HWREG16(0x40003C06)) 

This messed up the engine’s state when I wanted to write bytes. Anyway, it works now and here is some Energia code that outputs the same answer as a python script (which is included in the comments). The code needs a little work and I hope to set up an encrypted link between the MSP432 and the PC over the UART.

#include <msp432.h>
/*key and message obtained from NIST test vectors in http://csrc.nist.gov/groups/STM/cavp/documents/aes/AESAVS.pdf */
/* Using AES 128 ECB Encryption */
/* Tested against the following python code:

# This produces the same answer as http://aes.online-domain-tools.com/
# but not the same as NIST test vector for ECB
from Crypto.Cipher import AES
import binascii
key = binascii.unhexlify('8d2e60365f17c7df1040d7501b4a7b5a')
IV = 16 * '\x00'
mode = AES.MODE_ECB
encryptor = AES.new(key, mode, IV=IV)
text = binascii.unhexlify('59b5088e6dadc3ad5f27a460872d5929')
#text=b'Plain text msg  '
ciphertext = encryptor.encrypt(text)
print(binascii.hexlify(ciphertext))

 */
const char key[]="8d2e60365f17c7df1040d7501b4a7b5a";
const char testmsg[]="59b5088e6dadc3ad5f27a460872d5929";
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);
  Serial.println("Setting the following key: ");
  for (Index = 0; Index < 16; Index++)
  {
    
    KeySection = ByteStringToNumber(keystr+Index*2);
    Serial.print(KeySection,HEX);
    Serial.print(" ");
    *Ptr = (uint8_t) KeySection;   
    
  }
  Serial.println("");
  printAESRegisters();
}
void encryptBlock(const char *plain_text, char *crypto_text, uint32_t len)
{
  // Encrypts a 128 bit (16 byte) block of text
  int Index = 0;
  uint8_t Dummy = 0;
  uint32_t Section;
  volatile void *Ptr;
  // select encrypt mode  
  AESACTL0 = 0;//0xfffc;
  setKey(key); 
  Ptr = &AESADIN;
  Serial.println("Encrypting the following:" );
  for (Index = 0; Index < 16; Index++)
  {

    Section = ByteStringToNumber(plain_text+Index*2);
    Serial.print(Section,HEX);
    Serial.print(" ");
    *((uint8_t *)Ptr) = (uint8_t)Section;   
  }
  Serial.println(" ");
  delay(4);
  //while ( (AESACTL0 & (1 << 8) )==0); // wait for ready flag
  Ptr = &AESADOUT;
  for (Index = 0; Index < 16; Index ++)
  {
      crypto_text[Index] =  *((uint8_t *)Ptr);      
  } 
}


void printAESRegisters()
{
  Serial.print("AESASTAT: ");
  Serial.print(String(AESASTAT,16));
  Serial.print(", AESACTL0: ");
  Serial.print(String(AESACTL0,16));
  Serial.print(",  AESACTL1: ");
  Serial.println(String(AESACTL1,16));
}


// the setup routine runs once when you press reset:
void setup() {
  Serial.begin(38400);
}
char CryptoText[17];  
// the loop routine runs over and over again forever:
void loop() {
  int Index;
  CryptoText[16]=0;
  AESACTL0 = 0;
    
  encryptBlock(testmsg,CryptoText,16);  
  Serial.println("Just encrypted");
  printAESRegisters();
  Serial.println("Crypto Text:");
  
  for (Index = 0 ; Index < 16; Index ++ )
  {
    Serial.print(int(CryptoText[Index]),HEX);    
    Serial.print(" ");
  }
  Serial.println(" ");
  Serial.println(CryptoText);
  Serial.println("--------------------------------");

  delay(501);
}