SML — Secure Message Layer

API Reference

Complete technical reference for the SML C99 library. All public functions, types, constants, and integration patterns.

v1.0.0-stable ANSI C99 Zero Dependencies

Getting Started

Installation

Clone the repository and build the static library. SML has no dependencies beyond a C99 compiler and standard POSIX headers.

shell
git clone https://github.com/tlabs-technology/sml.git
cd sml
make          # → build/libsml.a
make test     # run test suite

Link against libsml.a and include sml/sml.h:

Makefile
CFLAGS  += -I/path/to/sml/include
LDFLAGS += -L/path/to/sml/build -lsml

Quick Start

A minimal example: two parties establish a session via X3DH and exchange one encrypted message.

quick_start.c
/* 1. Generate identities */
sml_identity_t alice, bob;
sml_identity_generate(&alice);
sml_identity_generate(&bob);

/* 2. Bob publishes a prekey bundle */
sml_prekey_bundle_t bundle;
sml_prekey_bundle_generate(&bob, &bundle);

/* 3. Alice opens a session with Bob's bundle */
sml_session_t alice_session;
sml_session_init(&alice_session, &alice);
sml_x3dh_initiate(&alice_session, &bundle);

/* 4. Alice encrypts */
uint8_t ciphertext[SML_MAX_MSG];
size_t  ct_len;
sml_encrypt(&alice_session,
            (const uint8_t *)"hello", 5,
            ciphertext, &ct_len);

/* 5. Transmit ciphertext to Bob ... */
/* 6. Bob decrypts (session established from init message) */
uint8_t plaintext[SML_MAX_MSG];
size_t  pt_len;
sml_decrypt(&bob_session, ciphertext, ct_len, plaintext, &pt_len);

Build Targets

Target Command Output
Static library make build/libsml.a
Shared library make shared build/libsml.so
Tests make test Exit 0 on pass
WebAssembly make wasm build/sml.wasm
iOS (arm64) make ios build/libsml-ios.a

Core API

Identity & Keys

An sml_identity_t holds a long-term Ed25519 keypair and the corresponding X25519 key for Diffie-Hellman. It is the root of trust for a participant.

sml.h — identity
/* Generate a new identity keypair */
sml_result_t sml_identity_generate(sml_identity_t *out);

/* Serialize / deserialize */
sml_result_t sml_identity_export(const sml_identity_t *id,
                                   uint8_t *buf, size_t *len);
sml_result_t sml_identity_import(sml_identity_t *out,
                                   const uint8_t *buf, size_t len);

/* Destroy — zeroes all key material */
void         sml_identity_free(sml_identity_t *id);

/* Generate a signed prekey bundle for publication */
sml_result_t sml_prekey_bundle_generate(const sml_identity_t *id,
                                        sml_prekey_bundle_t  *out);

X3DH Handshake

X3DH establishes a shared secret between two parties without requiring the recipient to be online. The initiator uses the recipient's prekey bundle; the responder processes the initiator's first message.

sml.h — x3dh
/* Initiator: consume remote bundle, produce init message */
sml_result_t sml_x3dh_initiate(sml_session_t         *s,
                               const sml_prekey_bundle_t *bundle);

/* Responder: process init message, derive same shared secret */
sml_result_t sml_x3dh_respond  (sml_session_t         *s,
                               const sml_init_message_t  *msg);

/* Retrieve the serialized init message for transmission */
sml_result_t sml_session_get_init_msg(const sml_session_t *s,
                                     sml_init_message_t *out);

NOTE

After sml_x3dh_initiate the session is in SML_SESSION_PENDING state. The first call to sml_encrypt transitions it to SML_SESSION_ACTIVE and attaches the init message to the ciphertext automatically.

Session Management

sml.h — session
/* Initialize a new session bound to an identity */
sml_result_t sml_session_init(sml_session_t         *s,
                              const sml_identity_t    *id);

/* Destroy a session — zeroes all ratchet state */
sml_result_t sml_session_free   (sml_session_t *s);

/* Serialize session state to opaque buffer (for storage) */
sml_result_t sml_session_serialize(const sml_session_t *s,
                                   uint8_t *buf, size_t *len);

/* Restore session from serialized buffer */
sml_result_t sml_session_deserialize(sml_session_t *s,
                                     const uint8_t  *buf, size_t len);

/* Query session state */
sml_state_t  sml_session_state(const sml_session_t *s);

Encrypt / Decrypt

sml.h — encrypt / decrypt
/*
 * sml_encrypt — encrypt plaintext using current ratchet state.
 *
 * ciphertext must be at least (pt_len + SML_OVERHEAD) bytes.
 * SML_OVERHEAD = 96 (header 32B + tag 16B + padding 48B max)
 */
sml_result_t sml_encrypt(sml_session_t *s,
                         const uint8_t  *plaintext,  size_t  pt_len,
                               uint8_t  *ciphertext, size_t *ct_len);

/*
 * sml_decrypt — decrypt ciphertext and advance ratchet.
 *
 * Handles out-of-order messages within the skipped-key window
 * (default: SML_MAX_SKIP = 512 messages).
 */
sml_result_t sml_decrypt(sml_session_t *s,
                         const uint8_t  *ciphertext, size_t  ct_len,
                               uint8_t  *plaintext,  size_t *pt_len);

Error Codes

All functions return sml_result_t. Zero is success.

Constant Value Meaning
SML_OK 0 Success
SML_ERR_INVALID_ARG -1 NULL pointer or invalid length
SML_ERR_INVALID_STATE -2 Session in wrong state for operation
SML_ERR_DECRYPT_FAIL -3 Authentication tag mismatch
SML_ERR_SKIP_OVERFLOW -4 Skipped-key cache exceeded SML_MAX_SKIP
SML_ERR_BUFFER_TOO_SMALL -5 Output buffer insufficient
SML_ERR_CRYPTO -6 Underlying crypto primitive failed
SML_ERR_OOM -7 Memory allocation failed

Integration

Swift — Swift Package Manager

Add the SML package to your Package.swift. The Swift wrapper provides a native, memory-safe API over the C ABI.

Package.swift
.package(url: "https://github.com/tlabs-technology/sml-swift.git",
         from: "1.0.0")
Swift usage
import SML

let alice = try SMLIdentity()
let bundle = try alice.generatePrekeyBundle()

var session = try SMLSession(identity: alice)
try session.initiateX3DH(with: bundle)

let ciphertext = try session.encrypt(Data("hello".utf8))

Kotlin — JNI

The Kotlin wrapper uses JNI to call into libsml.so. Include the AAR in your Gradle project.

build.gradle.kts
dependencies {
    implementation("technology.tlabs:sml-kotlin:1.0.0")
}

STATUS

The Kotlin/JNI wrapper is currently in development. Expected availability: Q2 2026.

JavaScript — Emscripten

Build the WebAssembly target with make wasm and load via the generated JavaScript glue module.

JavaScript
import SML from './sml.js';

const sml = await SML();
const alice = sml.identity_generate();
const bundle = sml.prekey_bundle_generate(alice);
const session = sml.session_init(alice);
sml.x3dh_initiate(session, bundle);

Security

Threat Model

SML is designed to protect against the following adversary classes:

A1

Passive Network Adversary

An adversary observing all ciphertext in transit cannot recover plaintext. Forward secrecy ensures past sessions remain safe even after private key compromise.

A2

Compromised Session Key

The Double Ratchet's DH ratchet provides break-in recovery: after a session key is compromised, subsequent DH steps heal the session within one ratchet step.

A3

Replay Attacks

Each message header includes a monotonically increasing message index. Re-delivered ciphertexts with the same index are rejected after the corresponding skipped key has been consumed.

Memory Safety

All key material lives in locked memory regions (via mlock on supported platforms) and is zeroed using a compiler-barrier-protected memset on release. Session state objects should be treated as secrets — do not write them to unencrypted storage without applying your platform's secure enclave or keychain primitives.

Best Practices

01

Always call sml_session_free when a session is no longer needed. Do not rely on process exit for key zeroing.

02

Rotate prekey bundles regularly. One-time prekeys should be used exactly once — remove a prekey from your server after it has been consumed by an initiator.

03

Do not reuse session objects across multiple remote peers. Each peer requires an independent sml_session_t.

04

Before production deployment, conduct an independent cryptographic audit. SML is research software — no warranty of fitness for production is expressed or implied.