# Exclude the cargo-fuzz workspace at `fuzz/` from the implicit single-crate
# workspace this package would otherwise form. The fuzz crate uses
# `libfuzzer-sys` (nightly + libFuzzer) and is built separately via
# `cargo +nightly fuzz run <target>` — see `fuzz/README.md`.
[workspace]
exclude = ["fuzz"]

[package]
name = "purecrypto"
version = "0.6.18"
edition = "2024"
rust-version = "1.88"
description = "A pure-Rust cryptography toolkit with no foreign-code dependencies, from constant-time primitives up to keys, X.509 and TLS."
repository = "https://github.com/KarpelesLab/purecrypto"
license = "MIT"
keywords = ["crypto", "cryptography", "no-std", "constant-time"]
categories = ["cryptography", "no-std::no-alloc"]

[lib]
name = "purecrypto"

[features]
# `std` is the convenient default; it implies `alloc`. Disable default
# features for a `no_std` build (`--no-default-features`), optionally
# re-enabling `alloc` for heap-backed types.
#
# `linux-getrandom` is in the default set so that, on Linux, entropy is drawn
# via getrandom(2) (which blocks until the kernel CSPRNG is seeded) rather than
# /dev/urandom (which does not), hardening the early-boot key-generation path.
# The feature is internally `#[cfg(target_os = "linux")]`-gated, so enabling it
# by default is inert on every non-Linux target and on unsupported Linux arches
# (where it transparently falls back to /dev/urandom).
default = ["std", "hash", "cipher", "kdf", "bignum", "rng", "rsa", "der", "dh", "x509", "pkcs12", "ec", "tls", "dtls", "mlkem", "mldsa", "slhdsa", "ascon", "aez", "lms", "xmss", "mac", "cert-compression", "embedded-roots", "cli", "linux-getrandom"]
std = ["alloc"]
alloc = []

# --- module gates (added as each layer lands) ---
hash = []
cipher = []
kdf = ["hash", "cipher"]
# Standalone MACs that aren't covered by `hash`/`kdf` (which already ship HMAC).
# Currently provides UMAC-64 / UMAC-128 (RFC 4418).
mac = ["cipher"]
bignum = []
rng = ["hash"]
# Opt-in: on Linux, draw OsRng bytes via getrandom(2) instead of reading from
# /dev/urandom. Hardens the early-boot path (getrandom(2) blocks until the
# kernel CSPRNG is initialised; /dev/urandom historically did not). Uses raw
# syscall instructions per arch — currently x86_64, aarch64, armv7, riscv64.
# On other Linux arches the feature is a no-op and OsRng falls back to
# /dev/urandom.
linux-getrandom = ["rng", "std"]
rsa = ["bignum", "rng", "hash"]
# hazmat: low-level secp256k1 scalar / point arithmetic + compressed SEC1 codec.
# NO semver-stability guarantee; off by default. Caller owns correctness and
# constant-time discipline. See `ec::secp256k1`.
hazmat-secp256k1 = ["ec"]
# Finite-field Diffie-Hellman over RFC 3526 MODP groups (group14..group18)
# plus RFC 4419 group-exchange. For SSH and legacy TLS / IKE interop;
# ECDH (in the `ec` module) is preferred for new code.
dh = ["bignum", "rng", "alloc"]
der = []
x509 = ["der", "rsa", "hash", "alloc", "ec"]
# PKCS#12 / PFX (RFC 7292) `.p12`/`.pfx` archives: parse + build the
# password-protected key+certificate bundles OpenSSL emits. Wraps PKCS#8
# (PBES2 for the shrouded key) and X.509 cert bags, sealed with the RFC 7292
# Appendix B SHA-based HMAC. Legacy `pbeWithSHAAnd3-KeyTripleDES-CBC` archives
# parse too (40-bit RC2 is not supported).
pkcs12 = ["x509", "kdf", "cipher", "der", "alloc"]
ec = ["bignum", "rng", "hash", "alloc"]
tls = ["cipher", "hash", "kdf", "ec", "rsa", "x509", "der", "rng", "mlkem", "alloc"]
# DTLS (RFC 6347 + RFC 9147). Builds on the `tls` stack — the record / replay /
# reassembly / cookie chassis lives in `src/dtls/`, with version-specific
# client and server state machines layered on top in later commits.
dtls = ["tls"]
# Legacy TLS interop: SSLv3 / TLS 1.0 / TLS 1.1 with CBC MAC-then-encrypt suites
# and static-RSA key transport. DEPRECATED and insecure (RFC 8996: MD5/SHA-1 PRF,
# CBC padding oracles, BEAST, POODLE) — provided ONLY for talking to legacy devices
# (e.g. VoIP-phone provisioning). Off by default; also requires lowering
# `Config::min_version` at runtime to actually negotiate these versions.
tls-legacy = ["tls"]
# QUIC v1 (RFC 9000) — transport-layer protocol over UDP, secured by TLS 1.3
# keys (RFC 9001), with loss recovery + congestion control (RFC 9002) and the
# unreliable DATAGRAM extension (RFC 9221). Builds on the `tls` stack; sans-I/O.
# Requires `std` for `std::sync::Mutex` used in the TLS-engine ⇄ QuicConnection
# hook bridge (the trait `QuicHooks: Send` forbids `Rc<RefCell<…>>`).
quic = ["tls", "std"]
# hazmat: low-level arithmetic exposures with NO semver-stability guarantee;
# the caller owns correctness and constant-time discipline. Off by default.
# `edwards25519::hazmat` exposes the order-L `Scalar` and the `EdwardsPoint`
# group over the shared curve25519 backend (for FROST / threshold callers).
hazmat-edwards25519 = ["ec"]
# ristretto255 (RFC 9496) — a STABLE public prime-order group module (not
# hazmat), the natural FROST dependency. Shares the order-L `Scalar` with
# `edwards25519::hazmat`.
ristretto255 = ["ec", "hash", "rng"]
# ML-KEM-768 (FIPS 203). no_std + no-alloc core: only hashing (SHA-3/SHAKE) and
# the RNG trait are required.
mlkem = ["hash", "rng"]
# Falcon / FN-DSA (FIPS 206 draft) — Falcon-512 and Falcon-1024: key
# generation, signing, and verification. Signing uses an emulated constant-time
# IEEE-754 double (FPEMU) for the FFT, LDL tree, and Gaussian sampler, so no
# hardware float is needed; `rng` supplies entropy for keygen/signing.
falcon = ["hash", "alloc", "rng"]
# ML-DSA (FIPS 204) signatures. Needs alloc for the variable-width signature
# encoding; the polynomial arithmetic itself is on the stack.
mldsa = ["hash", "rng", "alloc"]
# Unstable low-level ML-DSA access (`mldsa::hazmat`): raw NTT / field / sampling
# / packing / rounding primitives for threshold signing. NO semver guarantee;
# the caller owns correctness and constant-time discipline. Off by default.
hazmat-mldsa = ["mldsa"]
# SLH-DSA (FIPS 205) hash-based signatures. Needs alloc for the large
# signatures (up to ~50 KB); built purely from the hash primitives.
slhdsa = ["hash", "rng", "alloc"]
# Ascon (NIST SP 800-232 lightweight cryptography): Ascon-AEAD128 plus
# Ascon-Hash256 / Ascon-XOF128 / Ascon-CXOF128, all from one 320-bit
# permutation.
ascon = ["hash", "cipher"]
# AEZ v5 (Hoang-Krovetz-Rogaway): robust authenticated-encryption by enciphering,
# built from the AES round. Nonce-misuse-resistant, arbitrary expansion, vector
# AD. Needs `alloc` for the variable-length enciphered output and `hash` for the
# BLAKE2b key-derivation (`Extract`).
aez = ["cipher", "hash", "alloc"]
# LMS / HSS stateful hash-based signatures (RFC 8554, NIST SP 800-208).
# Stateful: the private key advances a one-time-key index on every signature
# and MUST be re-persisted after each sign — reuse is catastrophic.
lms = ["hash", "rng", "alloc"]
# XMSS / XMSS^MT stateful hash-based signatures (RFC 8391, NIST SP 800-208).
# Same stateful-key discipline as `lms`.
xmss = ["hash", "rng", "alloc"]
# HPKE (RFC 9180) hybrid public key encryption. Provides the DHKEM /
# HKDF / AEAD matrix used by OHTTP, MLS, and the ECH client-hello
# encryption. The full matrix: 4 KEMs (P-256, P-384, P-521, X25519) ×
# 3 KDFs (HKDF-SHA-{256,384,512}) × 3 AEADs (AES-128/256-GCM,
# ChaCha20-Poly1305) + ExportOnly, in all 4 modes (Base, PSK, Auth,
# AuthPSK).
hpke = ["hash", "kdf", "ec", "cipher", "rng", "alloc"]
# Encrypted Client Hello (draft-ietf-tls-esni-22). Hides the inner
# ClientHello (including SNI and ALPN) under an HPKE-sealed envelope
# bound to a public_name from a published `ECHConfigList`. Provides
# both client and server roles, retry_configs handling, and a
# bit-shape-identical GREASE producer for the no-config case.
ech = ["tls", "hpke"]
# RFC 8879 certificate compression (TLS 1.3 only). Pulls in the
# Karpeles-Lab `compcol` crate for the zlib (RFC 1950 over RFC 1951
# DEFLATE) codec — the sole vendored dependency, carved out from the
# crate's "no foreign code" charter because compcol is a sibling
# pure-Rust no_std project under the same maintainership. Algorithm 1
# (zlib) is the only codepoint wired today; brotli (2) and zstd (3)
# remain unimplemented.
cert-compression = ["tls", "dep:compcol"]
# Embedded root-CA trust store: pre-seed a `RootCertStore` with the curated
# first-party `cacrt` bundle — built from the Mozilla root program and others,
# following CA/Browser Forum rules (no filesystem access; portable across
# Linux/macOS/Windows). Enables `RootCertStore::with_embedded_roots`.
embedded-roots = ["tls", "dep:cacrt"]
# C ABI: builds the `ffi` module (the only place `unsafe` is allowed). Build a
# C library with `cargo rustc --features ffi --crate-type cdylib` (or staticlib).
ffi = ["std", "hash", "rng", "rsa", "ec", "der", "x509", "tls", "dtls", "quic", "mldsa", "slhdsa", "mlkem", "kdf", "cipher", "ascon", "lms", "xmss"]
# The `purecrypto` command-line tool (gates the binary target below).
cli = ["std", "hash", "rng", "rsa", "ec", "der", "x509", "tls", "dtls", "quic", "kdf", "cipher", "ascon", "lms", "xmss", "embedded-roots"]
# Async I/O surface for the sans-I/O TLS engine: `tls::tokio::TlsStream`
# (AsyncRead + AsyncWrite) that drives the handshake and awaits the
# device-signer `Readiness` via `tokio::io::unix::AsyncFd`. Off by default.
tokio = ["tls", "std", "dep:tokio"]
# Reactor I/O surface for the sans-I/O TLS engine: the signer `Readiness` as a
# `mio::event::Source` plus `tls::mio::drive_handshake`. Off by default.
mio = ["tls", "std", "dep:mio"]

# --- dependencies ---
# This crate's design principle is to ship no foreign code, primarily so the
# cryptographic critical path stays in-house and auditable. The single exception
# is `compcol` (also a Karpeles Lab project, MIT, pure-safe no_std + alloc),
# pulled in only under the `cert-compression` feature for RFC 8879 zlib certificate
# compression — re-implementing DEFLATE in-house just to satisfy the letter of
# the policy would burn ~2-3 kLoC of review surface for no security gain, since
# compcol sits outside the cryptographic trust boundary.
[dependencies]
compcol = { version = "0.4", default-features = false, features = ["zlib"], optional = true }
# Embedded root-CA bundle (curated from the Mozilla root program and others
# per CA/Browser Forum rules, parsed at build time into static DER).
# First-party (Karpeles Lab), `no_std`/no-alloc, fully-safe Rust with a
# small, well-defined surface — enabled by default rather than treated as a
# foreign-code dependency. Powers `RootCertStore::with_embedded_roots` under
# the `embedded-roots` feature.
cacrt = { version = "0.1", default-features = false, optional = true }
# Optional async/reactor I/O adapters for the sans-I/O TLS engine, each behind
# its own off-by-default feature. They are pure glue over the public
# `Connection`/`Readiness` surface and never compile unless requested, so the
# default build and `no_std` stay free of them. `net` provides tokio's
# `AsyncFd`; `os-ext` provides mio's `SourceFd`.
tokio = { version = "1", default-features = false, features = ["net", "io-util"], optional = true }
mio = { version = "1", default-features = false, features = ["os-ext", "net"], optional = true }

# Test-only: a full tokio runtime + `#[tokio::test]` for the `tokio` integration
# tests. Not shipped to consumers.
[dev-dependencies]
tokio = { version = "1", features = ["net", "io-util", "rt", "macros", "time"] }

[[bin]]
name = "purecrypto"
path = "src/bin/purecrypto/main.rs"
required-features = ["cli"]

[[example]]
name = "tls_ech_loopback"
required-features = ["ech"]

# External (TPM/HSM) identity signing via the suspend/resume seam.
[[example]]
name = "tls_external_signing"
required-features = ["tls"]

# Transparent pluggable private keys: one drive() loop for in-process or
# device (TPM/HSM) keys. Models the device with a unix-socket-backed thread.
[[example]]
name = "tls_private_key_driven"
required-features = ["tls", "std"]

# Legacy TLS 1.0/1.1 interop against the system `openssl` (run on demand:
# `cargo run --example tls_legacy_interop --features tls-legacy`).
[[example]]
name = "tls_legacy_interop"
required-features = ["tls-legacy", "std"]

# Ad-hoc throughput/latency harness used to find optimization choke points
# (`cargo run --release --example bench`). Gated so `--no-default-features`
# (no_std) builds don't try to compile it.
[[example]]
name = "bench"
required-features = ["std", "cipher", "hash", "ec", "rsa", "mlkem", "aez"]

[lints.rust]
# Constant-time correctness depends on disciplined arithmetic; keep the
# foundation honest.
missing_docs = "warn"
unreachable_pub = "warn"
# Deny (not forbid) so a future `ffi`/SIMD module can opt back in locally
# with a scoped `#[allow(unsafe_code)]`; the core stays safe.
unsafe_code = "deny"

[profile.release]
# Crypto code benefits from aggressive inlining of the small primitive ops.
lto = "thin"
