Skip to main content

C Guide

Overview

This page shows how to build the SST C library and its examples, then maps the example flow to the C API lifecycle used by native and embedded entities.

The C API is the primary path for native and embedded entities. It loads generated configs, requests session keys from Auth, performs the entity-to-entity secure handshake, sends and receives encrypted messages over sockets, and can encrypt arbitrary data buffers or persist session keys to disk.

The library lives in the standalone iotauth/sst-c-api repository.

Prerequisites

  • OpenSSL 3.0 or newer
  • CMake 3.19 or newer
  • C99-capable compiler
  • POSIX threads (pthreads)

On macOS:

brew install openssl@3 cmake

On Ubuntu:

sudo apt-get install libssl-dev cmake

Build and install the library

Clone the repository:

git clone https://github.com/iotauth/sst-c-api.git
cd sst-c-api

Build:

mkdir build && cd build
cmake ../
make

Install to /usr/local (header at /usr/local/include/sst-c-api/c_api.h, library at /usr/local/lib/libsst-c-api.a):

sudo make install

For verbose debug logs, build with the DEBUG configuration:

cmake -DCMAKE_BUILD_TYPE=DEBUG ../
make

Build the examples

Generate Auth credentials and configs from the main iotauth repository first:

cd examples # inside iotauth/iotauth
./generateAll.sh

Then build the C examples inside sst-c-api:

cd sst-c-api/examples/server_client_example
mkdir -p build && cd build
cmake ../
make

Each example directory has its own CMakeLists.txt.

Run the server/client example

Start Auth in one terminal:

cd auth/auth-server # inside iotauth/iotauth
mvn clean install
java -jar target/auth-server-jar-with-dependencies.jar -p ../properties/exampleAuth101.properties

Start the server in a second terminal:

cd sst-c-api/examples/server_client_example/build
./entity_server ../c_server.config

Start the client in a third terminal:

cd sst-c-api/examples/server_client_example/build
./entity_client ../c_client.config

The client requests session keys from Auth, performs the SST handshake with the server, and exchanges encrypted messages.

Programming model

Client lifecycle

#include "c_api.h"

// 1. Load config and credentials
SST_ctx_t* ctx = init_SST("../c_client.config");

// 2. Request session keys from Auth
session_key_list_t* keys = get_session_key(ctx, NULL);

// 3. Connect and perform SST handshake
SST_session_ctx_t* session = secure_connect_to_server(&keys->s_key[0], ctx);

// 4. Communicate
send_secure_message("hello", 5, session);

unsigned char buf[1024];
int n = read_secure_message(buf, session);

// 5. Clean up
free_session_ctx(session);
free_session_key_list_t(keys);
free_SST_ctx_t(ctx);

Server lifecycle

#include "c_api.h"

SST_ctx_t* ctx = init_SST("../c_server.config");
session_key_list_t* key_list = init_empty_session_key_list();

// Create and bind a TCP socket in application code, then:
int clnt_sock = accept(server_sock, ...);

// server_secure_comm_setup handles the SST handshake and key lookup
SST_session_ctx_t* session = server_secure_comm_setup(ctx, clnt_sock, key_list);

send_secure_message("ack", 3, session);

free_session_ctx(session);
free_session_key_list_t(key_list);
free_SST_ctx_t(ctx);

Receive thread

For concurrent receive loops, use the provided thread function:

#include <pthread.h>

pthread_t thread;
pthread_create(&thread, NULL, &receive_thread_read_one_each, (void*)session);

// ... send messages ...

pthread_cancel(thread);
pthread_join(thread, NULL);

receive_thread_read_one_each loops on read_secure_message and prints decrypted payloads until the thread is cancelled.

Buffer encryption (without a socket)

Encrypt and decrypt arbitrary buffers using a session key — useful for file encryption or offline data protection:

// With malloc (caller must free the output buffer)
unsigned char* enc = NULL;
unsigned int enc_len = 0;
encrypt_buf_with_session_key(&keys->s_key[0], plaintext, plain_len, &enc, &enc_len);
// ... use enc ...
free(enc);

// Without malloc (caller provides the output buffer)
unsigned char enc_buf[MAX_SIZE];
unsigned int enc_len = 0;
encrypt_buf_with_session_key_without_malloc(&keys->s_key[0],
plaintext, plain_len, enc_buf, &enc_len);

Config file format

Config files are generated by generateAll.sh and placed under entity/c/example_entities/configs/. The keys correspond to the config_t struct fields:

FieldPurpose
nameEntity name registered with Auth.
purposeSession-key purpose, e.g. {"group":"Servers"}.
numkeyNumber of session keys to request.
auth_idAuth ID.
auth_pubkey_pathPath to Auth public key certificate.
entity_privkey_pathPath to entity private key.
auth_ip_addrAuth host or IP address.
auth_port_numAuth TCP port.
entity_server_ip_addrTarget server host/IP (client entities).
entity_server_port_numTarget server port (client entities).
network_protocolDistribution protocol, usually TCP.
dist_cipher_key_pathPath to permanent distribution cipher key (if used).
dist_mac_key_pathPath to permanent distribution MAC key (if used).
file_system_manager_ip_addrFile System Manager host (IPFS examples).
file_system_manager_port_numFile System Manager port (IPFS examples).

Session key persistence

Session keys can be saved to disk and reloaded to avoid requesting new keys on every run:

// Save
save_session_key_list(keys, "keys.bin");

// Load (into an already-initialized list)
session_key_list_t* loaded = init_empty_session_key_list();
load_session_key_list(loaded, "keys.bin");

Password-protected variants are available via save_session_key_list_with_password and load_session_key_list_with_password.

Encryption modes

The AES_encryption_mode_t enum selects the AES cipher mode for both distribution keys and session keys:

ModeConstant
AES-128-CBCAES_128_CBC
AES-128-CTRAES_128_CTR
AES-128-GCMAES_128_GCM

hmac_mode_t controls HMAC authentication: USE_HMAC enables it, NO_HMAC disables it.

Memory and cleanup

The C API allocates context, key list, and session structures on the heap. Always free them:

free_session_ctx(session_ctx);
free_session_key_list_t(session_key_list);
free_SST_ctx_t(ctx);

Functions that allocate their output buffer (encrypt_buf_with_session_key, decrypt_buf_with_session_key, and their symmetric_* counterparts) document that the caller must free() the returned buffer.

Examples in the repository

ExampleLocationWhat it demonstrates
Server/clientexamples/server_client_example/Basic client-server secure communication with session keys and threaded receive.
File block encryptionexamples/file_block_encrypt_example/Encrypt 32 KB blocks of data with per-block session keys; verify via reader.
IPFS file sharingexamples/ipfs_examples/Encrypt, upload, and share files through IPFS with Auth-managed key distribution.

See the Server/Client Example, File Block Encryption, and IPFS File Sharing pages for full step-by-step instructions.

When to use C

Use C when:

  • the entity runs on a native or embedded target;
  • you need tight control over sockets and memory;
  • you need the file encryption or IPFS helpers;
  • you want to integrate SST into an existing C or C++ codebase.

Use Node.js when the entity is a gateway, scriptable service, or interactive application where event callbacks are more natural than direct socket management.