cachekit_core/lib.rs
1//! # cachekit-core
2//!
3//! LZ4 compression, xxHash3 integrity, AES-256-GCM encryption — for arbitrary byte payloads.
4//!
5//! This crate transforms bytes: compress them, verify their integrity, encrypt them.
6//! Bytes in, bytes out.
7//!
8//! ## Features
9//!
10//! | Feature | Description | Default |
11//! |:--------|:------------|:-------:|
12//! | `compression` | LZ4 compression via `lz4_flex` | Yes |
13//! | `checksum` | xxHash3-64 integrity verification | Yes |
14//! | `encryption` | AES-256-GCM (ring on native, aes-gcm on wasm32) + HKDF-SHA256 (hkdf) | No |
15//! | `ffi` | C header generation | No |
16//!
17//! ## Platform Support
18//!
19//! Compiles on both native targets and `wasm32-unknown-unknown` (Cloudflare Workers).
20//! On wasm32, encryption uses RustCrypto's `aes-gcm` (pure Rust) instead of `ring`.
21//! Both backends produce identical AES-256-GCM wire format.
22//!
23//! ## Quick Start
24//!
25//! ```rust,no_run
26//! use cachekit_core::ByteStorage;
27//!
28//! let storage = ByteStorage::new(None);
29//! let data = b"Hello, cachekit!";
30//!
31//! // Store: compress + checksum
32//! let envelope = storage.store(data, None).unwrap();
33//!
34//! // Retrieve: decompress + verify
35//! let (retrieved, _format) = storage.retrieve(&envelope).unwrap();
36//! assert_eq!(data.as_slice(), retrieved.as_slice());
37//! ```
38//!
39//! ## With Encryption
40//!
41//! ```rust,ignore
42//! use cachekit_core::{ZeroKnowledgeEncryptor, derive_domain_key};
43//! use zeroize::Zeroizing; // add the zeroize crate to your Cargo.toml
44//!
45//! fn main() -> Result<(), Box<dyn std::error::Error>> {
46//! // Derive tenant-isolated key
47//! // From your secret manager or CACHEKIT_MASTER_KEY, hex-decoded to 32 raw bytes.
48//! // Never hard-code it, and never pass the hex string's bytes.
49//! // Zeroizing wipes each key from memory when it is dropped.
50//! let master_key = Zeroizing::new(load_master_key_from_secret_manager()?);
51//! let tenant_key = Zeroizing::new(derive_domain_key(master_key.as_slice(), "cache", b"tenant-123")?);
52//!
53//! // Encrypt
54//! let encryptor = ZeroKnowledgeEncryptor::new()?;
55//! let ciphertext = encryptor.encrypt_aes_gcm(b"secret", tenant_key.as_slice(), b"tenant-123")?;
56//!
57//! // Decrypt
58//! let plaintext = encryptor.decrypt_aes_gcm(&ciphertext, tenant_key.as_slice(), b"tenant-123")?;
59//! assert_eq!(plaintext, b"secret");
60//! Ok(())
61//! }
62//! ```
63//!
64//! ## Security Properties
65//!
66//! - **AES-256-GCM**: Authenticated encryption via `ring` on native, `aes-gcm` on wasm32
67//! - **HKDF-SHA256**: Key derivation with tenant isolation (RFC 5869)
68//! - **xxHash3-64**: Fast non-cryptographic checksums (corruption detection), available standalone via [`checksum`]/[`verify_checksum`] without compression.
69//! - **Nonce safety**: Counter-based + random IV prevents reuse
70//! - **Memory safety**: `zeroize` on drop for all key material
71
72// Standalone integrity primitive (usable without compression/messagepack)
73#[cfg(feature = "checksum")]
74pub mod checksum;
75#[cfg(feature = "checksum")]
76pub use checksum::{checksum, verify_checksum};
77
78// Core byte storage layer
79pub mod byte_storage;
80pub use byte_storage::{ByteStorage, StorageEnvelope};
81
82// Unit-test allocation probe for the reject vectors (test builds only)
83#[cfg(all(test, feature = "compression", feature = "checksum"))]
84mod read_allocation_probe;
85
86// Structural pre-scan for untrusted MessagePack (no optional dependency)
87mod msgpack_bounds;
88pub use msgpack_bounds::{check_msgpack_structure, msgpack_document_len, MsgpackStructureError};
89
90// Encryption module (feature-gated)
91#[cfg(feature = "encryption")]
92pub mod encryption;
93#[cfg(feature = "encryption")]
94pub use encryption::{
95 derive_domain_key, EncryptionError, KeyDerivationError, KeyDomain, Keyring, TenantKeyring,
96 ZeroKnowledgeEncryptor, MAX_DECRYPT_ONLY_KEYS,
97};
98
99// C FFI layer (feature-gated)
100#[cfg(feature = "ffi")]
101pub mod ffi;
102#[cfg(feature = "ffi")]
103pub use ffi::CachekitError;