Skip to content

GilbertzRivi/lithium-core

Repository files navigation

lithium_core

Post-quantum hybrid cryptography and at-rest key management as a standalone Rust library.

Security status: pre-audit. This crate has not gone through an independent cryptographic audit and should not be used to protect sensitive production data yet.

Every construction combines a classical and a post-quantum primitive, so it stays secure as long as either one holds:

  • Encryption - X25519 + ML-KEM-1024, sealed with AES-256-GCM-SIV (crypto::kyberbox)
  • Signatures - Ed25519 + ML-DSA-87, both provided by crypto::sign
  • KDF / passwords - HKDF-SHA256, Argon2

The crate is #![deny(unsafe_code)] (the only unsafe is confined to secrets::arena, which wraps OS-locked memory behind a safe API - mmap/mlock on unix, VirtualAlloc/VirtualLock on Windows), secret types zeroize on drop, and all domain-separation labels are supplied by the caller as a validated crypto::Context - the crypto itself is application-agnostic.

Two pillars

1. At-rest key management (keys, secrets)

KeyManager owns an on-disk key store: it generates the hybrid identity, seals private keys under a master key from a pluggable MkProvider, and performs crash-safe MK rotation and rewrap automatically on a background thread. The only built-in provider stores the MK in cleartext and is gated behind the non-default insecure-plaintext-mk feature (dev/tests only); production callers supply password-backed, hardware-backed, TPM-backed, or application-specific sealing (see examples/password_mkprovider.rs and docs/mkprovider-examples.md). The MK is loaded from the provider once per operation by default; the non-default cached-mk feature unseals it once at startup and after each rotation and keeps it in the locked arena instead.

Secret types (SecByte32, SecretBytes, MasterKey32, ...) zeroize on drop.

By default KeyManager is fail-closed: it takes an exclusive lock on the store directory and locks its key material into RAM, refusing to start if either cannot be done. The non-default best-effort feature relaxes each for platforms that cannot honor them (e.g. Android, where flock is unsupported), proceeding on swappable memory or without the store lock and reporting the degraded state through a callback. A live second writer on the same store is still rejected.

2. Hybrid encryption (crypto)

crypto::kyberbox is the X25519 + ML-KEM-1024 AEAD construction: the KEM produces a fresh shared secret per message that is combined with the X25519 output through HKDF, so recovering the message key requires breaking both branches. See docs/combiner.md for the exact construction and its rationale, and docs/kyberbox.md for the wire format.

Helpers

Secondary, deployment-agnostic building blocks layered on the pillars: opaque (OPAQUE PAKE + export-key DEK wrapping), passwords (DEK generation), utils::store (TTL secret store).

Examples

cargo run --example kyberbox            # hybrid encrypt/decrypt round-trip
cargo run --example password_mkprovider # KeyManager identity, master key sealed under a passphrase

# dev-only: same lifecycle on the built-in cleartext master-key provider
cargo run --features insecure-plaintext-mk --example keyfile
use lithium_core::crypto::Context;
use lithium_core::crypto::kyberbox::DualEncryptionPrivateKey;
use lithium_core::secrets::SecretBytes;

// Domain separation, built from validated segments; the library adds the /v1.
let ctx = Context::base("myapp")?.add("message")?;

// The recipient's hybrid (X25519 + ML-KEM-1024) encryption keypair.
let (recipient_priv, recipient_pub) = DualEncryptionPrivateKey::ephemeral()?;

let body = SecretBytes::from_slice(b"Hello world!");

// `seal` draws a fresh reply keypair per call and bundles its public half
// into `sealed`, bound into the ciphertext so a MITM cannot swap it. It
// returns the matching reply secret: drop it for one-shot sealing, or keep
// it to open the recipient's reply. `aad` is an optional caller header
// bound into the ciphertext (b"" = none).
let (sealed, _reply_priv) = recipient_pub.seal(&ctx, b"", &body)?;

// `open` returns the plaintext and the reply public key the sender bundled in.
let (plain, _reply_pub) = recipient_priv.open(&ctx, b"", &sealed)?;

assert_eq!(plain.expose_as_slice(), body.expose_as_slice());

Security status

Not yet independently audited. The constructions, the hybrid-combiner rationale and the open questions for an auditor are documented under docs/: combiner.md, kyberbox.md, threat-model.md. The public API is intended to be frozen at 0.1 through the audit.

The library deliberately does not provide a whole application security model. In particular, the caller is responsible for authentic recipient public keys, unique domain-separation labels, replay protection, transport security, and deciding when to call key rotation.

To report a vulnerability, see SECURITY.md.

Contributing

Contributions are accepted under AGPL-3.0-only with a grant for commercial relicensing, and require a DCO sign-off - see CONTRIBUTING.md.

License

GNU AGPL-3.0-only, with a commercial license available (dual licensing) - see LICENSE.

About

Post-quantum hybrid cryptography and at-rest key management as a small, audit-focused Rust library.

Resources

License

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors