C Backend for High-Level Language Programming

A Tool Chain Example for Encrypted Data Journal

By Jordan Hrycaj on Thu, Mar 30, 2017
Category: Software,
Tags: c embedded iot nim programming

Updated: 18 Apr 2017

Introduction

I present a modern approach for developing portable/embedded software. Traditionally, this was the domain of coding in C/C++. Despite availability and flexibility, coding in either C or C++ comes with a price tag: Highly experienced, programming skills are needed (see also this comment.)

The idea is to keep all the advantages of plain C while at the same time working with a development system supporting functional and OO paradigms. Plain C code is generated by design which allows porting, optimising and debugging for the target system.

This is particularly useful for embedded systems, e.g. IoT.

summary

Encrypted Data Journal Project

I needed a small encryption tool for storing PAN scanner data journals safely. So I developed a crypto session library. As a proof of concept for this C/NIM article I wrote a little C programme that wraps this crypto session library.

Requirements

The encrypted data journals are produced by a programme using a built-in public key. Decryption and further processing is done by another programme with the corresponding private key built-in. There is a short term software cycle for these programmes and they are regularly replaced.

Threat Model

Encrypted journals are produced on-site, then transported to a safe site for decryption and further processing. Public/private key pairs can be regularly replaced with the binaries.

Solution Ingredients (will be used below)

  • XCrypt sources and C demo wrapper on GitHub.
  • Portal for the NIM Language

back to top

Implementation

I decided to use NIM, a high-level development language that produces portable C code by design rather than binaries. The C code compiles on popular and embedded systems and is easily interfaced with other C code.

So the development tool chain consist of

  • the NIM compiler and
  • the GCC compiler (many other C compilers would do)

The size for the compiled proof of concept binary is a bit more than 100KiB on my Debian/64bit machine. This is not bad for the generated C code given that it comprises a full blown cipher suite.

The Crypto Layer

At the core I use publicly available portable standard implementations in C for ECC and the ChaCha stream cipher. This allows to build a simple encryption suite accessible from NIM source code.

For my journal encryption requirements I wrote a simple (as opposed to X.509 or PGP/GPG) public/private session key management scheme in NIM which in turn is used to set up the data encryption module. The complete repository is available on GitHub.

For this proof of concept article I added a C/NIM session wrapper for the encryption module (here is the wrapper source).

Hybrid C/NIM Architecture

The NIM compiler will see the following source code architecture

   +------------------------------------------
   |   +--------------------------+                 NIM code
   |   | wrapper C/NIM interface  | ...
   |   |       NIM code           |                 glue code for
   |   +--------------------------+                 linking against C
   +-------------------------------------------
     |
     | refers to
     v
   +--------------------------------------------
   |   +----------+   +----------+                  NIM code
   |   |  XCrypt  |   | SessKey  | ...
   |   | NIM code |   | NIM code |                  facilitating
   |   +----------+   +----------+                  C code
   +--------------------------------------------
     |
     | refers to
     v
   +---------------------------------------------
   |   +--------+   +--------+   +--------+         C code
   |   | ChaCha |   | SHA256 |   |  uECC  | ...
   |   | C code |   | C code |   | C Code |         source code to
   |   +--------+   +--------+   +--------+         be imported to NIM
   +---------------------------------------------

and will derive/generate a pool of files with C source code. This pool of C sources can then be compiled directly against a C source containing a main() programme or be bundled into a linkable library. I chose the latter.

Interfacing With C

The crucial part of the main() function that will use the linkable library to run the crypto session looks like

   text = ...

   [...]

   /* generate keys */
   prv_key = prvkey ();
   pub_key = pubkey (prv_key);

   [...]

   /* encrypt */
   cipher_text = b64_encrypt (text, pub_key);
   printf ("\n*** Encrypted message:\n%s\n", cipher_text);

   /* decrypt */
   plain_text = b64_decrypt (cipher_text, prv_key);
   printf ("\n*** Decrypted message:\n%s\n", plain_text);

Here is the full C source code. And that is all that was needed.

back to top

DIY Building Process On Linux

Fetch the sources for the crypto lib and wrapper from GitHub with:

   git clone https://github.com/mjfh/nim-crypto.git

Preparation

GCC Compiler:
  It should be available through a package manager, eg apt.

NIM Compiler version 0.16 or better:
  On Debian/Linux 9 (Stretch) the NIM compiler can be installed via apt.
  On Ubuntu 16.04/Xenial, the NIM compiler is too old to be useful for compiling this example.
  For a generic Linux installation of the NIM compiler see Installation based on generated C code (you might also try the Docker image).

Compiling and running

Compile and run the C wrapper:

   cd nim-crypto
   make -f Makefile.simple
   ./src/cwrap
   ./src/cwrap "how much wood could a woodchuck chuck"
   make distclean

back to top

Summary

The advantage of using a modern tool for creating C code rather working in C directly is obvious. It reduces development time and allows for better code quality, testing and debugging.

Nevertheless, the C programming language development tools are universally available and come with sophisticated profiling, optimising, and debugging tools. Even older systems are mostly supported due to the long GCC history.

This makes C well positioned when portability and/or programme size is an issue. So C has an advantage when compared with other development tool chains like C++, Go, Rust, etc. which generate binaries directly by design. The strength of these tool chains are mostly eased out when portability is required and (in case of C++ and Go) the size of the produced binaries matters.

back to top