In this section we provide a complete example of how to build and run a Sancus application. We follow the example given in the paper: one protected module providing sensor data and one that transforms this data and signs it to be sent to the vendor.

Note that the example code below remains fully compatible with the official Sancus 2.0 release, as provided on the installation page. An updated example scenario with an MMIO sensor module providing authenticated readings from a time stamp counter peripheral is available on GitHub .

sensor.h#ifndef SENSOR_H
#define SENSOR_H

#include <sancus/sm_support.h>

extern struct SancusModule sensor;

typedef unsigned sensor_data_t;

sensor_data_t SM_ENTRY(sensor) read_sensor_data_t(void);

#endif
sensor.c#include "sensor.h"

DECLARE_SM(sensor, 0x1234);

static sensor_data_t SM_DATA(sensor) data;

sensor_data_t SM_ENTRY(sensor) read_sensor_data_t(void)
{
    return data;
}
reader.h#ifndef READER_H
#define READER_H

#include <sancus/sm_support.h>

#include "sensor.h"

extern struct SancusModule reader;

typedef struct
{
    char cipher[sizeof(sensor_data_t)];
    char tag[SANCUS_TAG_SIZE];
} ReaderOutput;

typedef unsigned nonce_t;

void SM_ENTRY(reader) get_readings(nonce_t no, ReaderOutput* out);

#endif
reader.c#include "reader.h"

#include <stdio.h>

DECLARE_SM(reader, 0x1234);

static sensor_data_t SM_FUNC(reader) transform_readings(sensor_data_t data)
{
    return data ^ 0xcafe;
}

int SM_FUNC(reader) outside_sm(void *p)
{
    return ( (p < (void*) &__PS(reader)) || (p >= (void*) &__PE(reader)) ) &&
           ( (p < (void*) &__SS(reader)) || (p >= (void*) &__SE(reader)) );
}

void SM_ENTRY(reader) get_readings(nonce_t no, ReaderOutput* out)
{
    /* Ensure output memory range about to be dereferenced lies outside SM. */
    if (!(outside_sm(out) && outside_sm(out + sizeof(ReaderOutput) - 1)))
    {
        puts("Illegal argument");
        return;
    }

    /* Securely verify and call sensor SM. */
    sensor_data_t data = read_sensor_data_t();

    /* Transform and seal sensor readings. */
    sensor_data_t transformed = transform_readings(data);

    if (!sancus_wrap(&no, sizeof(no), &transformed, sizeof(transformed),
                     out->cipher, out->tag))
    {
        puts("Error encrypting data");
    }
}
main.c#include <msp430.h>
#include <stdio.h>
#include <sancus/sm_support.h>
#include "reader.h"

void print_bytes(const char* bytes, size_t n)
{
    int i;
    for (i = 0; i < n; i++)
        printf("%02x", bytes[i] & 0xff);
}

int main()
{
    WDTCTL = WDTPW | WDTHOLD;

    sancus_enable(&sensor);
    sancus_enable(&reader);

    nonce_t no = 0xabcd;
    ReaderOutput out;
    get_readings(no, &out);

    printf("Nonce: ");
    print_bytes((char*)&no, sizeof(no));
    printf(", Cipher: ");
    print_bytes((char*)&out.cipher, sizeof(out.cipher));
    printf(", Tag: ");
    print_bytes(out.tag, sizeof(out.tag));
    putchar('\n');

    return 0;
}

int putchar(int c)
{
    P1OUT = c;
    P1OUT |= 0x80;

    return c;
}

For the build process, we assume a Bash shell is being used. We first define some variables:

ROM=48K
RAM=10K
STACK=256
VENDOR_ID=1234
NODE_KEY=deadbeefcafebabe

Compile the sources:

sancus-cc -c -o sensor.o sensor.c
sancus-cc -c -o reader.o reader.c
sancus-cc -c -o main.o main.c

Link everything in standalone mode:

sancus-ld --standalone --rom-size $ROM --ram-size $RAM --sm-stack-size $STACK -o main-no-mac.elf main.o reader.o sensor.o

Then we need to make sure the MAC sections are filled in in order for the reader module to be able to verify the sensor module. The first step is to calculate the vendor key:

sancus-crypto --gen-vendor-key $VENDOR_ID --key $NODE_KEY

Then we fill in the hash sections using this key:

sancus-crypto --fill-macs --key $VENDOR_KEY -o main.elf main-no-mac.elf

Where $VENDOR_KEY refers to the output of the previous command.

To simulate the resulting binary, simply run:

sancus-sim --rom-size $ROM --ram-size $RAM main.elf

To verify the output of the reader module, we first have to calculate its key:

sancus-crypto --gen-sm-key reader --key $VENDOR_KEY main.elf

Then we can decrypt the output of the reader module ($NONCE, $CIPHER, and $TAG refer to the data printed by the simulator and $SM_KEY refers to the output of the previous command):

sancus-crypto --unwrap $NONCE $CIPHER $TAG --key $SM_KEY