Wireless Encryption Between Galileo and MSP430

In this demo, the Galileo communicates with a MSP430G2553 microcontroller via the nRF24L01+ 2.4GHz transceiver. The MSP430 sends an encrypted block of data representing the state of six of its pins. This message is received by the sketch running on the Galileo and stored in shared memory. A Python script then wakes up, decrypts the message, and prints which keys were pressed to the screen. The purpose of this demo is to show how a simple sketch can take advantage of a different user space program on the Galileo to do something as complex as decryption.

Edit: There is now an Instructable version here. The updated code is now hosted on my GitHub – https://github.com/bunneydude/IPCBuffer (support for multiple readers/writers, larger buffers, full source code).

boards

flowcharts

MSP430

There are 6 pins on the MSP430 configured as inputs with their internal pull-up resistor connected to act as keys for the user to press. When a key is pressed (pin grounded by the long red wire shown wrapped around a pencil) or released (pin is pulled high by internal resistor) the MSP430 reads the state of all 6 pins, encrypts with AES-128 in ECB mode, and transmits to the Galileo. The board accepts up to 26V for input power and regulates it down to 3.3V with the LM2937. The nRF24L01+ (found on eBay for as low as $1 per board) draws about 26uA during standby, 11.3mA during transmit, and 13.5mA during receive (these values can be lowered by reducing the chip’s antenna output power and/or data rate). The radio is set to transmit at 2Mbps but can be programmed to 1Mbps or 250kbs to extend the range of the transmission. So far I’ve only tested indoors with direct line of sight up to about 15 feet. The AES code used comes from Texas Instruments. They make no promises about being safe from side channel attacks, but for now all I need is a correct implementation that will fit in the MSP430G2553′s limited memory. The library used to communicate with the nRF24L01+ comes from here with only a few modifications needed to port the code to the MSP430 and Galileo.

Sketch

The sketch running on the Galileo runs code similar to the MSP430 except that it handles receives from the nRF24L01+ instead of transmits. In the setup() method, the sketch creates three IPC objects: a shared memory segment one AES block length in size (16 bytes) and two semaphores. Together these form the control needed to make a queue with a single producer (sketch) and single consumer (Python script). Semaphore sem_space is initially 1 and represents how much space is available in shared memory for new encrypted messages. After receiving a new message, the sketch attempts to decrement the count of sem_space. The sketch will idle if sem_space is already 0 (indicating that the Python script hasn’t read the previous message out of shared memory). After storing the encrypted message in shared memory, the sketch increments sem_data (initially 0) to signal to the Python script that new data is available. You can enter ‘cat /proc/sysvipc/shm’ or ‘cat /proc/sysvipc/sem’ in the command prompt to see the current list of shared memory and semaphore objects, respectively.

Defining IPC object values (before setup())

#define SHM_SIZE 16 // bytes of shared memory to create
#define SHM_QUEUE_LENGTH 1 //number of AES blocks that will fit
int shmid; // shared memory for received encrypted data
int sid_data; // data in queue ready for decryption
int sid_space; // room in queue
// hard-coded keys
key_t key_shmid = 123456;
key_t key_sid_data = 654321;
key_t key_sid_space = 987654;    
uint8_t* shared_memory;

struct sembuf sem_down = {0, -1, 0};
struct sembuf sem_up = {0, 1, 0};

Setup IPC objects with calls to galileo_ipc library

// temp contains the error code: 0 on success, 1 or 2 on error
temp = startMem(&shmid, key_shmid, &shared_memory, SHM_SIZE);
temp = startSem(&sid_data, key_sid_data, 0);
temp = startSem(&sid_space, key_sid_space, SHM_QUEUE_LENGTH);

Writing to shared memory

if(nrf24_dataReady()){      
    nrf24_getData(rx_data_array);         
    semop(sid_space, &sem_down, 1); // -1 to space in queue
    memcpy(shared_memory, rx_data_array, 16);       
    semop(sid_data, &sem_up, 1);  // +1 to data in queue
}

The galileo_ipc library (WIP)

#include <stdint.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include "galileo_ipc.h" // currently only contains function prototypes

union semun{ 
  int val;
  struct semid_ds *buf;
  ushort *array;
  struct seminfo *__buf;
  void *__pad;
};

union semun semopts; 

uint8_t startMem(int* shmid, key_t key, uint8_t** mem, uint8_t size){
  // Setup shared memory
  if ((*shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0666)) < 0){
     return 1;
  }

  // Attach shared memory
  if ((*mem = (uint8_t *)shmat(*shmid, NULL, 0)) == (uint8_t *)-1){
     return 2;
  }
  return 0;
}

uint8_t startSem(int* sid, key_t key, uint8_t val){
  // Setup semaphore
  if ((*sid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0660)) < 0){
   return 1;
  }

  // Initialize count
  semopts.val = val;
  if ((semctl(*sid, 0, SETVAL, semopts)) < 0){
   return 2;
  }
  return 0;
}

Python

The Python script accesses the three IPC objects using their hard-coded key values (see the Limitations/Future Improvements section for details on this behavior). It then tries to decrement sem_data, going idle if the sketch has not placed any new encrypted message in shared memory. When the decrement to sem_data succeeds, the message is read from shared memory and sem_space is incremented so the sketch can place a new message in shared memory. In the code below, the strings “one”, “two”, etc are just large strings from an ASCII art generator. I’ve omitted them from this post to save space. I cross-compiled the PyCrypto and sysv_ipc Python modules from an Ubuntu virtual machine on my Windows 8 desktop.

from Crypto.Cipher import AES
import sysv_ipc
import os
numbers = {'1':one,'2':two,'3':three,'4':four,'5':five,'6':six,'0':zero}

keys = {'0x3b':1,'0x3d':2,'0x3e':3,'0x37':4,'0x2f':5,'0x1f':6,'0x3f':0}

key_shm = 123456
key_sem_data = 654321
key_sem_space = 987654

key = "000102030405060708090a0b0c0d0e0f".decode('hex')
obj = AES.new(key,AES.MODE_ECB)

# get ipc objects created by sketch
mem = sysv_ipc.SharedMemory(key_shm)
sem_data = sysv_ipc.Semaphore(key_sem_data) # initially 0
sem_space = sysv_ipc.Semaphore(key_sem_space) # initially 1

last_key = 0;
current_key = 0;

while(1):
    sem_data.acquire() #wait here until encrypted data is ready
    val = mem.read()  
    sem_space.release() #indicate data has been read from shm
    msg = obj.decrypt(val) 
    #print(map(hex,map(ord,msg))) #print decrypted data

    current_key = keys[hex(ord(msg[0]))]
    os.system('clear')
    if(current_key == 0):
        print("Key " + str(last_key) + " released.\n")
    else:
        print("Key " + str(current_key) + " pressed.\n")
        print(numbers[str(current_key)])
    last_key = current_key

Demo Video

I noticed the majority of the delay here was due to printing those large ASCII art numbers (connected over Ethernet to Galileo). Without the extra printing, the message of which key was pressed was nearly instantaneous (from the timescale of a human of course). I apologize for how dark the Galileo board looks – I brought a lamp over for more light but my phone still didn’t adjust fast enough.

Limitations/Future Improvements

While the MSP430 transmits the state of all 6 pins (i.e. multiple keys could be pressed at the same time) only single key presses are demonstrated in the video. It was annoying enough to keep both board and screen in frame while holding the camera and pressing different keys (eventually I’ll set up some sort of small tripod to make future videos easier).

As mentioned above, using ECB mode for AES isn’t the best idea in the world. Granted, the point of this demo was not to demonstrate a complete security solution (that would require a key exchange protocol, better AES mode, etc). Even then there would most likely be side channel attacks on both PyCrypto and whatever code is on the MSP430.

Instead of hard-coding keys for the IPC objects (done here for simplicity) you could use the ftok(3) function. However, there’s still the possibility for there to be a key collision. The sketch needs a reasonable way to respond to such an error so that the Python script doesn’t try to access an IPC object that doesn’t exist. The IPC_EXCL flag is OR’d in for shmget() and semget() to make sure you don’t end up opening an object created by a different program. I noticed that the IPC objects would be cleaned up if the sketch was killed but not if you re-programed the sketch. The best practice is to clean up the IPC objects yourself (I added a serial command that results in calls to shmdt() and shmctl() to take care of this). If you leave off the IPC_EXCL flag for this demo, the sketch will still acquire the old IPC objects and run properly since it re-initializes the semaphores. However, there might still exist a race condition with the Python script if you don’t kill it before re-programming the sketch.

Data only flows from the sketch to the Python script here due to the nature of the data. It would be trivial to copy and modify the above code to set up another shared memory producer-consumer queue. This would be useful in a project where the sketch needs to use the decrypted data to transmit a new message over the radio. Increasing SHM_QUEUE_LENGTH and SHM_SIZE appropriately would allow for the sketch to load more than one message for the Python script to decrypt.

The galileo_ipc library will be formatted so that conforms to the built-in Arduino libraries. That way its functions will be in a familiar form (something like IPC.createShm() with syntax highlighting).  I’ll need IPC in my next project so I will be updating galileo_ipc along the way (switching from SysV to POSIX-style IPC objects/functions for starters).

About these ads
1 comment
  1. You should look in to Elliptic Curve Cryptography as an alternative to ECB mode AES. You can use much smaller keys which makes the computations much more efficient in embedded applications. Check the algorithm you use though since apparently some of the most widely distributed implementations include a deliberate weakness (See wikipedia article on ECC).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: