The simulator provides a convenient way to experiment with Sancus. Based on Icarus Verilog, it is basically a wrapper around the iverilog command, taking care of the necessary file conversions. Invoking the simulator looks as follows:

sancus-sim [--rom-size SIZE] [--ram-size SIZE] FILE
Here, FILE should be a valid ELF binary for the MSP430 and SIZE should be a number in bytes or a number followed by K to specify an amount in kilobytes. Make sure the amount of ROM/RAM matches the amount used when linking the binary. By default, the amount of ROM/RAM is 48K/10K.

The Sancus compiler driver tries to mimic GCC as closely as possible. Therefore, sancus-cc can be used us a drop-in replacement for gcc in most cases. The main difference is that sancus-cc will only perform the compile step which basically means the -c flag is mandatory. Also, only one file can be compiled at a time. A typical invocation looks as follows:

sancus-cc -c [GCC ARGS...] FILE
The compiler will generate protected modules based on annotations that should be added to the source code. The header file <sancus/sm_support.h>, installed as part of the compiler, provides the following annotations:

  • SM_ENTRY(name) declares an entry point.
  • SM_FUNC(name) declares a function that is not an entry point.
  • SM_DATA(name) declares data that should be part of the module's secret section.

Here, name must be a valid C identifier uniquely identifying the software module. This name is only used by the compiler to differentiate modules; it is never used by the Sancus hardware.

Like the compiler, the Sancus linker tries to mimic standard tools, LD in this case. A typical invocation looks as follows:

sancus-ld [LD ARGS...] [--rom-size SIZE] [--ram-size SIZE] [--standalone] [--sm-stack-size SIZE] FILE...

The ROM/RAM sizes are as above. The --sm-stack-size argument defines how much space should be allocated in the secret section for the stack.

The linker has two "output modes". If the --standalone flag is passed to the linker, it will create a fully linked binary that can be directly loaded into the simulator or FPGA. Multiple protected modules can be linked together and a main function has to be provided. Also, any libc functions used by the binary will be linked in.

In the second mode, when this flag is not passed, the linker creates a relocatable binary which cannot be directly loaded. Instead, it is intended to be loaded and linked by host software already running on a Sancus node. Multiple protected modules, as well as unprotected code, can be added and libc functions can also be called. However, library functions will not be added to the binary and should be resolved on the host. Currently, a main function is not supported in this mode which means that after a binary is loaded, the host software should provide the means to call an entry point of one of the protected modules.

The sancus-crypto script provides a convenient way to perform all needed crypto operations. All input keys and data should be provided in hexadecimal notation without the leading 0x.

To compute a vendor key:

sancus-crypto --gen-vendor-key ID --key NODE_KEY

To compute the key of a software module:

sancus-crypto --gen-sm-key SM_NAME --key VENDOR_KEY ELF_FILE

Here, SM_NAME is the name of the module as given to the compiler using the annotations.

To encrypt some plaintext (BODY) using associated data (AD):

sancus-crypto --wrap AD BODY --key KEY

And the reverse operation:

sancus-crypto --unwrap AD CIPHER TAG --key KEY

Finally, the following can be used for binaries created using the --standalone linker mode. This will automatically fill all the hash sections used for secure linking in the binary:

sancus-crypto --fill-macs --key VENDOR_KEY -o OUT_FILE IN_FILE

Currently, all modules need to have the same vendor ID for this to work.

Together with Sancus, a number of tools will be installed that make working with the FPGA a bit easier. These tools are just rebranded versions of the standard openMSP430 tools.

Loader sancus-loader

Sancus supports a simple UART protocol to load binaries which can be used using the sancus-loader script:
sancus-loader [-device DEV] [-baudrate BAUD] ELF/IHEX FILE

GDB proxy sancus-gdbproxy

The GDB proxy translates between the GDB protocol and the UART-based debug protocol of the openMSP430.
sancus-gdbproxy [-device DEV] [-baudrate BAUD] [-port PORT] [-shell]
(If the -shell flag is not given, a simple GUI will be opened to configure the proxy.) This will start a proxy listening on port PORT to which you can connect with GDB:
$ msp430-gdb
(gdb) target remote localhost:PORT
(gdb) ...

Debug GUI sancus-minidebug

Run sancus-minidebug to open a simple debug GUI. This GUI allows you to inspect registers and memory and control the CPU (reset, step,...).

This section gives an overview of the instructions added to the core MSP430 instruction set. The hexadecimal number next to the names of the instructions are their opcodes.

The header <sancus/sm_support.h> contains a number of convenience functions to call these instructions from C-code. These functions are also listed here. Next to these functions, this header also declares a number of types, the meaning of which should be clear:

typedef unsigned sm_id;
typedef unsigned vendor_id;

struct SancusModule
{
    sm_id       id;
    vendor_id   vendor_id;
    const char* name;
    void*       public_start;
    void*       public_end;
    void*       secret_start;
    void*       secret_end;
};

The macro DECLARE_SM(name, vendor_id) is provided to declare and initialize a struct SancusModule. This struct can then be used in some of the API calls.

unprotect 0x1380

Deactivates the hardware protection of the currently executing module. That is, the module whose public section contains the current value of the program counter.

Convenience wrapper:

void sancus_disable(void);

protect SP, layout 0x1381

Activates the hardware protection for a new module with the given layout. The boundaries of the public section are given in r12 and r13 and those of the secret section in r14 and r15. (Note that half-open intervals are used for the boundaries.) SP, the ID of the software provider, is given in r11. If the protection is successfully activated, the ID that Sancus has assigned to the new module is returned in r15; otherwise, 0 is returned.

Convenience wrapper:

sm_id sancus_enable(struct SancusModule* sm);

This function returns the ID of the module if the protection was successfully actived, 0 otherwise.

attest address, expected hash 0x1382

The hash of the identity of the module whose public section contains address (r14), is calculated and compared with the value stored at expected hash (r15). If the hashes are equal, the ID of the verified module is returned in r15, otherwise 0 is returned.

Convenience wrapper:

sm_id sancus_verify_address(const void* expected_hash, struct SancusModule* sm);

encrypt AD start, AD end, BODY start, BODY end, CIPHER result, TAG result 0x1384

Encrypts, using the key of the calling module, the data from BODY start (r12) until BODY end (r13) using the data from AD start (r10) until AD end (r11) as associated data. The resulting ciphertext is stored at the address pointed to by CIPHER result (r14) and the tag at the address pointed to by TAG result (r15). If something fails, for example when this instruction is called from unprotected code, 0 is returned in r15.

Convenience wrapper:

int sancus_wrap(const void* ad, size_t ad_len, const void* body, size_t body_len, void* cipher, void* tag);

decrypt AD start, AD end, CIPHER start, CIPHER end, TAG, BODY result 0x1384

Decrypts, using the key of the calling module, the data from CIPHER start (r12) until CIPHER end (r13) using the data from AD start (r10) until AD end (r11) as associated data and the data at TAG (r15) as MAC. The resulting plaintext is stored at the address pointed to by BODY result (r14). If something fails, for example when this instruction is called from unprotected code or the tag is incorrect, 0 is returned in r15.

Convenience wrapper:

int sancus_unwrap(const void* ad, size_t ad_len, const void* cipher, size_t cipher_len, const void* tag, void* body);

get-id address 0x1385

Returns the ID of the module whose public section contains address (r15) in r15.

Convenience wrapper:

sm_id sancus_get_id(void* addr);