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);
}

Command line compiling

A student asked how to use the standard C libraries with one of my examples recently. He wanted to know how to use string functions such as strcat, strcpy and so on. This isn’t something I had tried before because my goal was to expose the lower level details of hardware I/O. Anyway, having fiddled around a bit on the command line I came up with this command for buidling code for the STM32L011 nucleo board

arm-none-eabi-gcc -static -mthumb -mcpu=cortex-m0plus main.c init.c serial.c -T linker_script.ld -o main.elf -nostartfiles

Breaking this down:
arm-none-eabi-gcc: Your cross compiler. Your PATH environment variable should include the directory this lives in.
-static: Don’t use DLL’s (run-time linking) i.e. include the library function code in the final executable image.
-mthumb: Generate thumb code rather than ARM code.
-mcpu=cortex-m0plus: The STM32L011 has a Cortex M0+ CPU
The list of C files
-T linker_script.ld: Use this linker script to define memory regions when building the output executable image.
-o main.elf: name the output file main.elf
–nostartfiles: Don’t include “standard” start and stop functions. The code includes our own custom start-up code

I wanted to be able to dump the output file onto the virtual disk emulated by the nucleo board. This is done as follows:

arm-none-eabi-objcopy -O binary main.elf main.bin

This creates a raw binary program image which can be dropped on to the virtual disk.
The code was based a previous serial example for the STM32L011. The main.c file is replaced with this:


/* 
 * Serial: serial i/o routines for the STM32L011
*/


#include "stm32l011.h"
#include "serial.h"
#include <string.h>
void delay(int);

void delay(int dly)
{
  while( dly--);
}

void initClockHSI16()
{
    // Use the HSI16 clock as the system clock - allows operation down to 1.5V
        RCC_CR &= ~BIT24;
        RCC_CR |= BIT0; // turn on HSI16 (16MHz clock)
        while ((RCC_CR & BIT2)==0); // wait for HSI to be ready
        // set HSI16 as system clock source 
        RCC_CFGR |= BIT0;
}
void configPins()
{
	// Enable PORTB where LED is connected
	RCC_IOPENR |= BIT1;
	GPIOB_MODER |= BIT6; // make bit3  an output
	GPIOB_MODER &= ~BIT7; // make bit3  an output
}	
const char Str1[]="Hello ";
const char Str2[]="World\r\n";
char CombinedString[50];
int main()
{
        uint32_t Counter=0;
	initClockHSI16();
	configPins(); 
        initUART(9600);
	CombinedString[0]=0;
        CombinedString[49]=0;
        
	while(1)
	{
		GPIOB_ODR |= BIT3;
		delay(2000000);
		GPIOB_ODR &= ~BIT3;
		delay(2000000);
                strcpy(CombinedString,Str1);           
                strcat(CombinedString,Str2);
                eputs(CombinedString);                
                eputs("\r\n");                
	} 
	return 0;
}