libcrypt - Unified Cryptography Library

Cryptographic primitives wrapping mbedTLS and libsodium

libcrypt - Unified Cryptography Library

Overview

libcrypt provides all cryptographic primitives used by OctoMY. It wraps two vendored C libraries — mbedTLS 3.6 LTS for RSA/AES/hashing and libsodium 1.0.21 for Ed25519/X25519/XChaCha20-Poly1305 — behind a clean C++ API in the oc_crypt namespace.

This library replaces the former libmbedtls (raw C sources) and libqpolarssl (Qt wrapper) with a single unified library.

Architecture

libcrypt/
├── mbedtls/           # Vendored mbedTLS 3.6.4 LTS (crypto-only, no TLS)
├── psa/               # PSA Crypto API headers (required by mbedTLS 3.6)
├── crypt/             # OctoMY C++ wrapper (oc_crypt namespace)
│   ├── CryptRandom    # CSPRNG (CTR-DRBG + entropy)
│   ├── CryptHash      # SHA-256, SHA-512, etc.
│   ├── CryptCipher    # AES-CBC, AES-GCM, etc.
│   ├── CryptPki       # RSA sign/verify/encrypt/decrypt
│   ├── CryptEcc       # Ed25519, X25519, XChaCha20-Poly1305
│   └── CryptTypes     # Algorithm enums and conversions
├── sodium-prefix/     # Pre-built libsodium 1.0.21 (headers + static lib)
├── build-sodium.sh    # Build script for libsodium
└── lib.qbs

Dependency Graph

libfs    ──► libcrypt  (AES-XTS for encrypted filesystem)
libsec   ──► libcrypt  (RSA/Ed25519 for key management)
libcomms ──► libsec    (KeyStore, ISigningKey for sessions)

API Reference

All classes are in the oc_crypt namespace.

CryptRandom

CSPRNG using mbedTLS CTR-DRBG with platform entropy.

oc_crypt::Random rng;
QByteArray bytes = rng.random(32);      // 32 random bytes
int result = rng.update(customSeed);    // re-seed
mbedtls_ctr_drbg_context *ctx = rng.context();  // for mbedTLS API calls

CryptHash

Cryptographic hashing (SHA-256, SHA-512, SHA-1, MD5, RIPEMD-160, etc.).

oc_crypt::Hash hasher(oc_crypt::THash::SHA_256);
hasher.update(data1);
hasher.update(data2);
QByteArray digest = hasher.finish();

// One-shot
QByteArray hash = oc_crypt::Hash::hash(data, oc_crypt::THash::SHA_256);

// HMAC
QByteArray mac = oc_crypt::Hash::hmac(key, data, oc_crypt::THash::SHA_256);

CryptCipher

Symmetric encryption (AES-256-CBC, AES-256-GCM, Camellia, etc.).

oc_crypt::Cipher enc(oc_crypt::TCipher::AES_256_CBC, oc_crypt::TCipherDir::ENCRYPT);
enc.setIV(iv);
enc.setKey(key);
QByteArray ciphertext = enc.crypt(plaintext);

oc_crypt::Cipher dec(oc_crypt::TCipher::AES_256_CBC, oc_crypt::TCipherDir::DECRYPT);
dec.setIV(iv);
dec.setKey(key);
QByteArray decrypted = dec.crypt(ciphertext);

CryptPki

RSA public-key cryptography (key generation, sign/verify, encrypt/decrypt, PEM export/import).

oc_crypt::Pki pki;
pki.generateKeyPair(4096);

// Sign
QByteArray signature = pki.sign(message, oc_crypt::THash::SHA_256);

// Verify (with public key only)
oc_crypt::Pki verifier;
verifier.parsePublicKey(pki.getPEMPubkey());
int result = verifier.verify(message, signature, oc_crypt::THash::SHA_256);
// result == 0 means valid

// Encrypt/Decrypt
QByteArray ciphertext = verifier.encrypt(plaintext);
QByteArray decrypted = pki.decrypt(ciphertext);

// PEM export
QByteArray pubPem = pki.getPEMPubkey();
QByteArray privPem = pki.getPEMKey();

CryptEcc

Ed25519 signing, X25519 key exchange, and XChaCha20-Poly1305 AEAD via libsodium.

// Initialize once at startup
oc_crypt::Ecc::initialize();

// Ed25519 key generation
QByteArray pub, sec;
oc_crypt::Ecc::generateEd25519KeyPair(pub, sec);
// pub = 32 bytes, sec = 64 bytes

// Deterministic keygen from seed
QByteArray seed(32, '\x00');  // 32-byte seed
oc_crypt::Ecc::generateEd25519KeyPairFromSeed(seed, pub, sec);

// Sign and verify
QByteArray signature = oc_crypt::Ecc::ed25519Sign(message, sec);
bool valid = oc_crypt::Ecc::ed25519Verify(message, signature, pub);

// X25519 key exchange (convert Ed25519 keys first)
QByteArray xPub = oc_crypt::Ecc::ed25519PublicToX25519(pub);
QByteArray xSec = oc_crypt::Ecc::ed25519SecretToX25519(sec);
QByteArray shared = oc_crypt::Ecc::x25519KeyExchange(myXSec, theirXPub);

// XChaCha20-Poly1305 AEAD
QByteArray key = oc_crypt::Ecc::generateAeadKey();     // 32 bytes
QByteArray nonce = oc_crypt::Ecc::generateAeadNonce();  // 24 bytes
QByteArray ct = oc_crypt::Ecc::aeadEncrypt(plaintext, key, nonce, additionalData);
QByteArray pt = oc_crypt::Ecc::aeadDecrypt(ct, key, nonce, additionalData);

CryptTypes

Algorithm enumerations and conversion utilities.

Category Available Algorithms
Hash (THash) SHA_256, SHA_512, SHA_384, SHA_224, SHA_1, MD5, RIPEMD_160
Cipher (TCipher) AES_128/192/256_ECB/CBC/CFB128/CTR, AES_128/256_GCM, CAMELLIA variants

Concept-based API (octomy::crypt)

A second, forward-looking API surface lives in the octomy::crypt namespace. Each cryptographic category — AEAD, hashing, KDF, signatures, key exchange, raw block ciphers, erasure coding — is expressed as a C++20 concept. Concrete primitives satisfy the concept; consumer code is templated on it.

This is the API the internal Sentient Global Hive crypto-strategy spec (§CS-4) builds on. It coexists with the oc_crypt namespace above; existing call sites stay on oc_crypt until they migrate.

Concept set

src/libs/libcrypt/crypt/concepts/
├── Aead.hpp           # authenticated encryption with associated data
├── Hash.hpp           # Hash, IncrementalHash, MerkleHash (refinement chain)
├── Kdf.hpp            # key derivation
├── Signature.hpp      # detached signature schemes
├── Kex.hpp            # key exchange
├── BlockCipher.hpp    # raw block primitive (legacy modes)
└── ErasureCodec.hpp   # parameterized (n, k) shard codec

The exact concept definitions are reproduced from §CS-4.1; each header carries the canonical text plus a comment describing the eventual concrete satisfiers.

Templated consumer code

Consumers depend on the concept, not on a specific primitive:

#include "crypt/concepts/Aead.hpp"

template<octomy::crypt::Aead A>
QByteArray sealShard(typename A::Key key, typename A::Nonce nonce,
                     QByteArrayView shardBytes) {
    return A::seal(key, nonce, shardBytes, /*aad=*/{});
}

// Caller picks the primitive at the type-name boundary:
auto ct = sealShard<octomy::crypt::ChaCha20Poly1305>(k, n, plaintext);

A runtime selector exists for cases that need it (e.g. activity configuration choosing an algorithm from a stored enum) — see §CS-4.3.

Refinement: Hash → IncrementalHash → MerkleHash

Hash is one-shot. IncrementalHash adds update / finalize and refines Hash. MerkleHash adds deriveChildHash / merkleProof and refines IncrementalHash. A MerkleHash-satisfying type automatically satisfies the other two. The L3 shard codec depends on MerkleHash because per-shard Merkle proofs are part of the on-disk shard format.

Concrete satisfiers landed

Class Concept Backing Reference
ChaCha20Poly1305 Aead mbedTLS RFC 8439
XChaCha20Poly1305 Aead Monocypher draft-irtf-cfrg-xchacha
Sha256 IncrementalHash mbedTLS FIPS-180
Sha512 IncrementalHash mbedTLS FIPS-180
Hkdf<H> Kdf mbedTLS (templated on a Hash satisfier) RFC 5869
Ed25519 Signature Monocypher RFC 8032
X25519 Kex Monocypher RFC 7748

Every satisfier carries a static_assert(Concept<T>) at the bottom of its header — the concept design and the concrete implementation are checked against each other at compile time.

Status

Phase Scope Status
C0 Concept headers + concept-instance tests Landed
C1a AEAD-ratchet primitives (Sha256/512, Hkdf, Ed25519, X25519, XChaCha20-Poly1305) Landed
C1b RSA (Signature) + AES (BlockCipher) satisfiers Pending
C2 Extract Galois-field + Reed-Solomon → ErasureCodec impl Pending
C3 Vendor BLAKE3 → MerkleHash satisfier Pending
C4 ChaCha20-Poly1305 (RFC 8439) → Aead satisfier Landed
C5 Elligator2 Pending
C6 Residual primitive audit Pending
C7–C8 OpenSSL retirement Pending
C9 PQ primitives stocked-unused Pending

See the internal crypto-strategy.md spec for the full plan.

Build Requirements

libsodium

libsodium must be built once before compiling. Run the provided script:

cd src/libs/libcrypt
./build-sodium.sh

This downloads libsodium 1.0.21, builds a static library, and installs headers and libsodium.a to sodium-prefix/. The script is idempotent.

mbedTLS

mbedTLS 3.6.4 LTS sources are vendored directly — no separate build step needed. A custom mbedtls_config.h enables only the crypto modules OctoMY uses (no TLS, X.509, or networking).

Namespace

All wrapper classes use oc_crypt:: (not crypt::) to avoid collision with the POSIX crypt() function on Linux.

Test Coverage

  • testCryptEcc (16 tests): Ed25519 keygen/sign/verify, X25519 key exchange, XChaCha20-Poly1305 AEAD, invalid input handling
  • testKey (6 tests): RSAKey allocation, integrity, signing, hybrid encryption
  • testKeyFactory (21 tests): Key creation, serialization, sign/verify, encrypt/decrypt for both RSA and Ed25519 via ISigningKey interface
  • testKeyStore (4 tests): Key persistence and retrieval
  • testCommsSession (3 tests): Session crypto operations
  • testCourierEncryption (7 tests): AEAD replay window, corrupted ciphertext, truncation, reorder recovery, key mismatch
Topics
reference libcrypt cryptography security