[package]
name = "libsoliton"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
authors.workspace = true
description = "Core cryptographic library for the LO protocol — hybrid post-quantum key exchange, signatures, ratchet, and storage encryption"
categories = ["cryptography"]
keywords = ["post-quantum", "x-wing", "ed25519", "double-ratchet", "ml-kem"]

[lib]
name = "soliton"

# All dependencies are exact-pinned (=x.y.z) for supply-chain attack prevention
# and reproducible builds. Cargo.lock alone is insufficient — it can be
# regenerated, losing the pin. Exact versions here are the source of truth.
#
# RC dependencies: sha3, hmac, hkdf (0.11/0.13-rc), kem (0.3.0-pre),
# ml-dsa (0.1.0-rc). These are the RustCrypto PQ ecosystem — stable releases
# are pending upstream FIPS 203/204 finalization. Exact pins prevent silent
# updates; track https://github.com/RustCrypto for stable releases.
#
# sha3 0.10.x has no `zeroize` feature — the feature was added in 0.11.x rc.
# Since HMAC and HKDF use Sha3_256 as a keyed state (containing key material),
# we must use sha3 0.11.x rc to get ZeroizeOnDrop on the hash state.
#
# Transitive dep pins (not imported directly):
#
#   keccak 0.2.0 (2026-03-16) removed the `p1600` function in favor of a new
#   `Keccak` struct API. sha3 0.11.0-rc.7 still calls `keccak::p1600` and
#   fails to compile against keccak 0.2.0. Pin to the last version with the
#   old API. Remove once sha3 publishes an rc compatible with keccak 0.2.0.
#
#   digest 0.11.2 (2026-03-13) and crypto-common 0.2.1 (2026-02-25) removed
#   `BlockSizes` (re-exported as `digest::common::BlockSizes`); digest 0.11.2
#   also changed `Clone` handling in `(Reset)MacTraits`. sha3 0.11.0-rc.7
#   imports `BlockSizes`; hmac/hkdf 0.13.0-rc.5 require `Hmac<H>: Clone`.
#   Pin to the snapshot versions published on the same day as sha3/hmac/hkdf rc.
#   Remove once sha3/hmac/hkdf publish versions compatible with the new APIs.
[dependencies]
zeroize = { workspace = true }
thiserror = { workspace = true }
ruzstd = "=0.8.2"
sha3 = { version = "=0.11.0-rc.7", features = ["zeroize"] }
hmac = { version = "=0.13.0-rc.5", features = ["zeroize"] }
hkdf = "=0.13.0-rc.5"
keccak = "=0.2.0-rc.2"
digest = "=0.11.0-rc.11"
crypto-common = "=0.2.0-rc.15"
chacha20poly1305 = { workspace = true }
# chacha20poly1305 propagates chacha20/zeroize but NOT poly1305/zeroize.
# This explicit dependency activates zeroization of the Poly1305 r,s key
# pair and accumulator state after each AEAD operation.
poly1305 = { workspace = true }
argon2 = { version = "=0.5.3", features = ["zeroize"] }
# No explicit backend selected: auto-detection picks fiat_u64_backend (formally
# verified constant-time field arithmetic) on 64-bit stable, fiat_u32_backend on
# 32-bit, simd on nightly x86_64 with AVX2. All backends are constant-time.
curve25519-dalek = { version = "=4.1.3", features = ["zeroize"] }
subtle = { workspace = true }
# Three getrandom versions in the dep tree: 0.2 (multiple transitive), 0.3 (direct),
# 0.4 (tempfile transitive). All use the same OS syscall; the duplication is
# unavoidable until upstream crates align on a single version.
getrandom = "=0.3.4"
# Pinned to 2.2.0: 2.2.1 pulls newer digest transitive deps that break zeroize
# feature propagation through ml-kem/ml-dsa. Do not bump without verifying the
# full zeroize feature chain across the dependency tree.
ed25519-dalek = { version = "=2.2.0", default-features = false, features = ["zeroize", "std"] }
# ml-kem/ml-dsa depend on rc digest/sha3/signature stacks (0.11.x/0.11.x/3.x)
# while ed25519-dalek/chacha20poly1305 use stable stacks (0.10.x/0.10.x/2.x).
# This causes ~15 duplicate crate pairs (digest, sha2, sha3, block-buffer,
# crypto-common, hybrid-array, signature, etc). Unavoidable until ml-kem/ml-dsa
# publish against the stable digest stack. Does not affect correctness — the two
# stacks are isolated at the type level and never share state.
ml-kem = { version = "=0.2.3", features = ["zeroize", "deterministic"] }
kem = "=0.3.0-pre.0"
ml-dsa = { version = "=0.1.0-rc.7", features = ["zeroize"] }
# rand_core is pulled transitively by ml-kem (0.6) and ml-dsa (0.10).
# Deterministic/seed-based APIs + getrandom are used to avoid RNG trait issues.

[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
getrandom = { version = "=0.3.4", features = ["wasm_js"] }
# ml-kem → rand_core 0.6 → getrandom 0.2.17. On wasm32-unknown-unknown,
# getrandom 0.2 without the `js` feature produces a compile_error!. This
# dependency exists solely to activate the feature — soliton never imports
# getrandom_02 directly. Cargo unifies features by package name (not alias).
getrandom_02 = { package = "getrandom", version = "=0.2.17", features = ["js"] }

[features]
# Enables internal accessors for white-box integration testing only.
# Not part of the public API — never enable in production.
test-utils = []

[dev-dependencies]
# Self-reference with test-utils so integration tests can call test-only accessors
# (e.g. RatchetState::root_key_bytes). Has no effect on normal library builds.
libsoliton = { path = ".", features = ["test-utils"] }
hex-literal = "=1.1.0"
hex = "=0.4.3"
proptest = "=1.10.0"
# Used exclusively in the parallel streaming benchmark (stream_encrypt_*mib_parallel).
# Provides a pre-warmed thread pool so b.iter() measures encryption throughput,
# not thread spawn cost. Dev-only — never compiled into the library.
rayon = "=1.11.0"
# ml-dsa is a direct dependency, re-declared here so integration tests can call
# sign_internal directly to produce deterministic F.27 test vectors (pinned rnd).
ml-dsa = { version = "=0.1.0-rc.7", features = ["zeroize"] }

[[bench]]
name = "bench"
harness = true

[build-dependencies]
# sha2 0.10 (stable) for build-time wordlist hash verification only.
# Build scripts run on the host and don't link into the final binary.
sha2 = "=0.10.9"
