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:
| Field | Purpose |
|---|---|
name | Entity name registered with Auth. |
purpose | Session-key purpose, e.g. {"group":"Servers"}. |
numkey | Number of session keys to request. |
auth_id | Auth ID. |
auth_pubkey_path | Path to Auth public key certificate. |
entity_privkey_path | Path to entity private key. |
auth_ip_addr | Auth host or IP address. |
auth_port_num | Auth TCP port. |
entity_server_ip_addr | Target server host/IP (client entities). |
entity_server_port_num | Target server port (client entities). |
network_protocol | Distribution protocol, usually TCP. |
dist_cipher_key_path | Path to permanent distribution cipher key (if used). |
dist_mac_key_path | Path to permanent distribution MAC key (if used). |
file_system_manager_ip_addr | File System Manager host (IPFS examples). |
file_system_manager_port_num | File 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:
| Mode | Constant |
|---|---|
| AES-128-CBC | AES_128_CBC |
| AES-128-CTR | AES_128_CTR |
| AES-128-GCM | AES_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
| Example | Location | What it demonstrates |
|---|---|---|
| Server/client | examples/server_client_example/ | Basic client-server secure communication with session keys and threaded receive. |
| File block encryption | examples/file_block_encrypt_example/ | Encrypt 32 KB blocks of data with per-block session keys; verify via reader. |
| IPFS file sharing | examples/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.